* tests for user provider

* username is no longer required
* added ip address property to user
This commit is contained in:
tmont 2011-02-10 21:04:13 +00:00
parent 7e042d3b93
commit 9a1f0347c0
14 changed files with 208 additions and 12 deletions

View File

@ -1,7 +1,14 @@
using JetBrains.Annotations; using JetBrains.Annotations;
namespace VideoGameQuotes.Api { namespace VideoGameQuotes.Api {
/// <summary>
/// Provides an interface for identifying the current user
/// </summary>
public interface ICurrentUserProvider { public interface ICurrentUserProvider {
/// <summary>
/// Returns the user currently performing actions on the site, or <c>null</c>
/// if the user cannot be identified
/// </summary>
[CanBeNull] [CanBeNull]
User CurrentUser { get; } User CurrentUser { get; }
} }

View File

@ -6,7 +6,8 @@
<generator class="identity" /> <generator class="identity" />
</id> </id>
<property name="Username" column="username" not-null="true" type="string" length="50" unique="true"/> <property name="Username" column="username" not-null="false" type="string" length="50"/>
<property name="IpAddress" column="ip_address" not-null="false" type="string" length="40"/>
<property name="Created" column="created" not-null="true" type="DateTime" /> <property name="Created" column="created" not-null="true" type="DateTime" />
<property name="Group" column="user_group" not-null="true" /> <property name="Group" column="user_group" not-null="true" />

View File

@ -7,7 +7,7 @@
</id> </id>
<property name="Created" column="created" not-null="true" /> <property name="Created" column="created" not-null="true" />
<property name="Value" column="point_value" not-null="true" /> <property name="Direction" column="direction" not-null="true" type="int" length="1" />
<many-to-one name="Voter" column="voter_id" not-null="true" foreign-key="fk_vote_user"/> <many-to-one name="Voter" column="voter_id" not-null="true" foreign-key="fk_vote_user"/>
<many-to-one name="Quote" column="quote_id" not-null="true" foreign-key="fk_vote_quote" /> <many-to-one name="Quote" column="quote_id" not-null="true" foreign-key="fk_vote_quote" />

View File

@ -1,11 +1,17 @@
using System.Linq; using System;
using System.Linq;
using JetBrains.Annotations;
using NHibernate; using NHibernate;
using Portoa.NHibernate; using Portoa.NHibernate;
using Portoa.Persistence; using Portoa.Persistence;
namespace VideoGameQuotes.Api.Persistence { namespace VideoGameQuotes.Api.Persistence {
public interface IUserRepository : IRepository<User> { public interface IUserRepository : IRepository<User> {
[CanBeNull]
User FindByUsername(string name); User FindByUsername(string name);
[CanBeNull]
User FindByIpAddress(string ipAddress);
} }
public class UserRepository : NHibernateRepository<User>, IUserRepository { public class UserRepository : NHibernateRepository<User>, IUserRepository {
@ -14,5 +20,9 @@ namespace VideoGameQuotes.Api.Persistence {
public User FindByUsername(string name) { public User FindByUsername(string name) {
return Records.FirstOrDefault(user => user.Username == name); return Records.FirstOrDefault(user => user.Username == name);
} }
public User FindByIpAddress(string ipAddress) {
return Records.FirstOrDefault(user => user.IpAddress == ipAddress);
}
} }
} }

View File

@ -1,10 +1,12 @@
using Portoa.Persistence; using System;
using Portoa.Persistence;
namespace VideoGameQuotes.Api.Persistence { namespace VideoGameQuotes.Api.Persistence {
public interface IUserService { public interface IUserService {
User Save(User user); User Save(User user);
User FindByUsername(string name); User FindByUsername(string name);
User FindByIpAddress(string ipAddress);
} }
public class UserService : IUserService { public class UserService : IUserService {
@ -23,5 +25,10 @@ namespace VideoGameQuotes.Api.Persistence {
public User FindByUsername(string name) { public User FindByUsername(string name) {
return repository.FindByUsername(name); return repository.FindByUsername(name);
} }
[UnitOfWork]
public User FindByIpAddress(string ipAddress) {
return repository.FindByIpAddress(ipAddress);
}
} }
} }

View File

@ -13,6 +13,7 @@ namespace VideoGameQuotes.Api {
public virtual DateTime Created { get; set; } public virtual DateTime Created { get; set; }
public virtual string Username { get; set; } public virtual string Username { get; set; }
public virtual string IpAddress { get; set; }
public virtual UserGroup Group { get; set; } public virtual UserGroup Group { get; set; }
public virtual void ChangePassword([DoNotLog]string newPassword) { public virtual void ChangePassword([DoNotLog]string newPassword) {

View File

@ -18,7 +18,7 @@ namespace VideoGameQuotes.Web {
Container Container
.AddNewExtension<LogAllMethodCalls>() .AddNewExtension<LogAllMethodCalls>()
.RegisterType<ICurrentUserProvider, UserProvider>() .RegisterType<ICurrentUserProvider, SessionBasedUserProvider>()
.RegisterType<IUserService, UserService>() .RegisterType<IUserService, UserService>()
.RegisterType<IUserRepository, UserRepository>(); .RegisterType<IUserRepository, UserRepository>();
} }

View File

@ -4,12 +4,12 @@ using VideoGameQuotes.Api;
using VideoGameQuotes.Api.Persistence; using VideoGameQuotes.Api.Persistence;
namespace VideoGameQuotes.Web.Security { namespace VideoGameQuotes.Web.Security {
public class UserProvider : ICurrentUserProvider { public class SessionBasedUserProvider : ICurrentUserProvider {
private readonly IUserService userService; private readonly IUserService userService;
private readonly ISessionStore sessionStore; private readonly ISessionStore sessionStore;
private readonly HttpContextBase httpContext; private readonly HttpContextBase httpContext;
public UserProvider(IUserService userService, ISessionStore sessionStore, HttpContextBase httpContext) { public SessionBasedUserProvider(IUserService userService, ISessionStore sessionStore, HttpContextBase httpContext) {
this.userService = userService; this.userService = userService;
this.sessionStore = sessionStore; this.sessionStore = sessionStore;
this.httpContext = httpContext; this.httpContext = httpContext;
@ -21,15 +21,15 @@ namespace VideoGameQuotes.Web.Security {
if (user == null) { if (user == null) {
//identify user by IP address //identify user by IP address
var username = httpContext.Request.UserHostAddress; var ipAddress = httpContext.Request.UserHostAddress;
if (string.IsNullOrEmpty(username)) { if (string.IsNullOrEmpty(ipAddress)) {
return null; return null;
} }
user = userService.FindByUsername(username); user = userService.FindByIpAddress(ipAddress);
if (user == null) { if (user == null) {
user = new User { user = new User {
Username = username, IpAddress = ipAddress,
Group = UserGroup.User Group = UserGroup.User
}; };

View File

@ -86,7 +86,7 @@
<DependentUpon>Default.aspx</DependentUpon> <DependentUpon>Default.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType> <SubType>ASPXCodeBehind</SubType>
</Compile> </Compile>
<Compile Include="Security\UserProvider.cs" /> <Compile Include="Security\SessionBasedUserProvider.cs" />
<Compile Include="Global.asax.cs"> <Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon> <DependentUpon>Global.asax</DependentUpon>
</Compile> </Compile>

View File

@ -34,6 +34,9 @@
<Reference Include="log4net"> <Reference Include="log4net">
<HintPath>..\..\Lib\log4net.dll</HintPath> <HintPath>..\..\Lib\log4net.dll</HintPath>
</Reference> </Reference>
<Reference Include="Moq">
<HintPath>..\..\Lib\Moq.dll</HintPath>
</Reference>
<Reference Include="MySql.Data"> <Reference Include="MySql.Data">
<HintPath>..\..\Lib\MySql.Data.dll</HintPath> <HintPath>..\..\Lib\MySql.Data.dll</HintPath>
</Reference> </Reference>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VideoGameQuotes.Web.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("VideoGameQuotes.Web.Tests")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2e52aa26-c170-44ad-801f-3ee9d6aa3fcd")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,48 @@
using System.Web;
using Moq;
using NUnit.Framework;
using Portoa.Web.Session;
using VideoGameQuotes.Api;
using VideoGameQuotes.Api.Persistence;
using VideoGameQuotes.Web.Security;
namespace VideoGameQuotes.Web.Tests {
[TestFixture]
public class UserProviderTests {
[Test]
public void Should_use_user_in_session_store() {
var session = new Mock<ISessionStore>();
var user = new User();
session.SetupGet(s => s["user"]).Returns(user).Verifiable();
var userProvider = new SessionBasedUserProvider(new Mock<IUserService>().Object, session.Object, new Mock<HttpContextBase>().Object);
Assert.That(userProvider.CurrentUser, Is.EqualTo(user));
session.VerifyAll();
}
[Test]
public void Should_identify_user_using_ip_address() {
var session = new Mock<ISessionStore>();
session.SetupGet(s => s["user"]).Returns(null);
var httpContext = new Mock<HttpContextBase>();
var httpRequest = new Mock<HttpRequestBase>();
httpRequest.SetupGet(req => req.UserHostAddress).Returns("10.4.60.120");
httpContext.SetupGet(ctx => ctx.Request).Returns(httpRequest.Object);
var userService = new Mock<IUserService>();
userService.Setup(service => service.Save(It.IsAny<User>())).Callback<User>(user => {
Assert.That(user, Is.Not.Null);
Assert.That(user.Username, Is.Null);
Assert.That(user.IpAddress, Is.EqualTo("10.4.60.120"));
Assert.That(user.Group, Is.EqualTo(UserGroup.User));
}).Returns(new User { Id = 1 });
var userProvider = new SessionBasedUserProvider(userService.Object, session.Object, httpContext.Object);
Assert.That(userProvider.CurrentUser, Is.EqualTo(new User { Id = 1 }));
}
}
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5787E8CA-072D-4231-B34A-B4C92BEEA3E7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VideoGameQuotes.Web.Tests</RootNamespace>
<AssemblyName>VideoGameQuotes.Web.Tests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Moq">
<HintPath>..\..\Lib\Moq.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>..\..\Lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="Portoa">
<HintPath>..\..\Lib\Portoa.dll</HintPath>
</Reference>
<Reference Include="Portoa.Web">
<HintPath>..\..\Lib\Portoa.Web.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UserProviderTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Src\VideoGameQuotes.Api\VideoGameQuotes.Api.csproj">
<Project>{329FAB1F-A18D-4B7B-9E3C-A0C157E55503}</Project>
<Name>VideoGameQuotes.Api</Name>
</ProjectReference>
<ProjectReference Include="..\..\Src\VideoGameQuotes.Web\VideoGameQuotes.Web.csproj">
<Project>{5D576303-EEB3-409B-80E2-FC221C23D7BF}</Project>
<Name>VideoGameQuotes.Web</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoGameQuotes.Api.Tests",
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoGameQuotes.Web", "Src\VideoGameQuotes.Web\VideoGameQuotes.Web.csproj", "{5D576303-EEB3-409B-80E2-FC221C23D7BF}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoGameQuotes.Web", "Src\VideoGameQuotes.Web\VideoGameQuotes.Web.csproj", "{5D576303-EEB3-409B-80E2-FC221C23D7BF}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoGameQuotes.Web.Tests", "Tests\VideoGameQuotes.Web.Tests\VideoGameQuotes.Web.Tests.csproj", "{5787E8CA-072D-4231-B34A-B4C92BEEA3E7}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -25,6 +27,10 @@ Global
{5D576303-EEB3-409B-80E2-FC221C23D7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5D576303-EEB3-409B-80E2-FC221C23D7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D576303-EEB3-409B-80E2-FC221C23D7BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D576303-EEB3-409B-80E2-FC221C23D7BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D576303-EEB3-409B-80E2-FC221C23D7BF}.Release|Any CPU.Build.0 = Release|Any CPU {5D576303-EEB3-409B-80E2-FC221C23D7BF}.Release|Any CPU.Build.0 = Release|Any CPU
{5787E8CA-072D-4231-B34A-B4C92BEEA3E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5787E8CA-072D-4231-B34A-B4C92BEEA3E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5787E8CA-072D-4231-B34A-B4C92BEEA3E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5787E8CA-072D-4231-B34A-B4C92BEEA3E7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE