Building out user services

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4039351
This commit is contained in:
loudej
2009-11-10 08:43:28 +00:00
parent fe63e8fc1c
commit 6fd0589f72
18 changed files with 319 additions and 14 deletions

View File

@@ -31,10 +31,39 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Autofac, Version=1.4.4.561, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\autofac\Autofac.dll</HintPath>
</Reference>
<Reference Include="FluentNHibernate, Version=1.0.0.593, Culture=neutral, PublicKeyToken=8aa435e3cb308880, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\FluentNHibernate.dll</HintPath>
</Reference>
<Reference Include="NHibernate, Version=2.1.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\NHibernate.dll</HintPath>
</Reference>
<Reference Include="NHibernate.ByteCode.Castle, Version=2.1.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\NHibernate.ByteCode.Castle.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.5.2.9222, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\nunit\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core"> <Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\sqlite\System.Data.SQLite.DLL</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq"> <Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
@@ -46,9 +75,21 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Users\Services\MembershipServiceTests.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Users\" /> <ProjectReference Include="..\Orchard.Tests\Orchard.Tests.csproj">
<Project>{ABC826D4-2FA1-4F2F-87DE-E6095F653810}</Project>
<Name>Orchard.Tests</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Web\Packages\Orchard.Users\Orchard.Users.csproj">
<Project>{79AED36E-ABD0-4747-93D3-8722B042454B}</Project>
<Name>Orchard.Users</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard\Orchard.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autofac.Builder;
using Autofac.Modules;
using NHibernate;
using NUnit.Framework;
using Orchard.Data;
using Orchard.Models;
using Orchard.Models.Driver;
using Orchard.Models.Records;
using Orchard.Security;
using Orchard.Users.Models;
using Orchard.Users.Services;
namespace Orchard.Tests.Packages.Users.Services {
[TestFixture]
public class MembershipServiceTests {
private IMembershipService _membershipService;
private ISessionFactory _sessionFactory;
private ISession _session;
public class TestSessionLocator : ISessionLocator {
private readonly ISession _session;
public TestSessionLocator(ISession session) {
_session = session;
}
public ISession For(Type entityType) {
return _session;
}
}
[TestFixtureSetUp]
public void InitFixture() {
var databaseFileName = System.IO.Path.GetTempFileName();
_sessionFactory = DataUtility.CreateSessionFactory(
databaseFileName,
typeof(ModelRecord),
typeof(ModelTypeRecord));
}
[TestFixtureTearDown]
public void TermFixture() {
}
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterModule(new ImplicitCollectionSupportModule());
builder.Register<MembershipService>().As<IMembershipService>();
builder.Register<DefaultModelManager>().As<IModelManager>();
builder.Register<UserDriver>().As<IModelDriver>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
_session = _sessionFactory.OpenSession();
builder.Register(new TestSessionLocator(_session)).As<ISessionLocator>();
var container = builder.Build();
_membershipService = container.Resolve<IMembershipService>();
}
[Test]
public void CreateUserShouldAllocateModelAndCreateRecords() {
var user = _membershipService.CreateUser(new CreateUserParams("a", "b", "c", null, null, true));
Assert.That(user.UserName, Is.EqualTo("a"));
Assert.That(user.Email, Is.EqualTo("c"));
}
}
}

View File

@@ -136,6 +136,15 @@ namespace Orchard.Tests.Models {
} }
[Test]
public void CreateShouldMakeModelAndModelTypeRecords() {
var beta = _manager.New("beta");
_manager.Create(beta);
var modelRecord = _container.Resolve<IRepository<ModelRecord>>().Get(beta.Id);
Assert.That(modelRecord, Is.Not.Null);
Assert.That(modelRecord.ModelType.Name, Is.EqualTo("beta"));
}
private ModelRecord CreateModelRecord(string modelType) { private ModelRecord CreateModelRecord(string modelType) {
var modelRepository = _container.Resolve<IRepository<ModelRecord>>(); var modelRepository = _container.Resolve<IRepository<ModelRecord>>();

View File

@@ -21,7 +21,7 @@ namespace Orchard.Tests.Mvc {
public void Init() { public void Init() {
var builder = new ContainerBuilder(); var builder = new ContainerBuilder();
builder.Register<ReplacementFooController>().As<IController>() builder.Register<ReplacementFooController>().As<IController>()
.Named("controller.orchard.web.foo") .Named("controller.orchard.foo")
.FactoryScoped(); .FactoryScoped();
var container = builder.Build(); var container = builder.Build();

View File

@@ -0,0 +1,11 @@
using Orchard.Models.Driver;
namespace Orchard.Users.Models {
public class UserDriver : ModelDriver {
protected override void New(NewModelContext context) {
if (context.ModelType == "user") {
WeldModelPart<UserModel>(context);
}
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.Models;
using Orchard.Security;
namespace Orchard.Users.Models {
public class UserModel : ModelPart, IUser {
public string UserName { get; set; }
public string Email { get; set; }
}
}

View File

@@ -63,17 +63,25 @@
<ItemGroup> <ItemGroup>
<Compile Include="Controllers\AdminController.cs" /> <Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\HomeController.cs" /> <Compile Include="Controllers\HomeController.cs" />
<Compile Include="Models\UserModel.cs" />
<Compile Include="Models\UserDriver.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\MembershipService.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Package.txt" /> <Content Include="Package.txt" />
<Content Include="Web.config" /> <Content Include="Web.config" />
<Content Include="Views\Web.config" /> <Content Include="Views\Web.config" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="App_Data\" /> <Folder Include="App_Data\" />
<Folder Include="Content\" /> <Folder Include="Content\" />
<Folder Include="Models\" />
<Folder Include="Scripts\" /> <Folder Include="Scripts\" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.Models;
using Orchard.Security;
using Orchard.Users.Models;
namespace Orchard.Users.Services {
public class MembershipService : IMembershipService {
private readonly IModelManager _modelManager;
public MembershipService(IModelManager modelManager) {
_modelManager = modelManager;
}
public void ReadSettings(MembershipSettings settings) {
// accepting defaults
}
public IUser CreateUser(CreateUserParams createUserParams) {
var user = _modelManager.New("user").As<UserModel>();
user.UserName = createUserParams.Username;
user.Email = createUserParams.Email;
_modelManager.Create(user);
return user;
}
public IUser GetUser(string username) {
throw new NotImplementedException();
}
}
}

View File

@@ -17,14 +17,14 @@ namespace Orchard.Controllers {
// This constructor is not used by the MVC framework but is instead provided for ease // This constructor is not used by the MVC framework but is instead provided for ease
// of unit testing this type. See the comments at the end of this file for more // of unit testing this type. See the comments at the end of this file for more
// information. // information.
public AccountController(IFormsAuthentication formsAuth, IMembershipService service) { public AccountController(IFormsAuthentication formsAuth, IMembershipServiceShim service) {
FormsAuth = formsAuth ?? new FormsAuthenticationService(); FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService(); MembershipService = service ?? new AccountMembershipService();
} }
public IFormsAuthentication FormsAuth { get; private set; } public IFormsAuthentication FormsAuth { get; private set; }
public IMembershipService MembershipService { get; private set; } public IMembershipServiceShim MembershipService { get; private set; }
public ActionResult LogOn() { public ActionResult LogOn() {
return View(); return View();
@@ -238,7 +238,7 @@ namespace Orchard.Controllers {
#endregion #endregion
} }
public interface IMembershipService { public interface IMembershipServiceShim {
int MinPasswordLength { get; } int MinPasswordLength { get; }
bool ValidateUser(string userName, string password); bool ValidateUser(string userName, string password);
@@ -246,7 +246,7 @@ namespace Orchard.Controllers {
bool ChangePassword(string userName, string oldPassword, string newPassword); bool ChangePassword(string userName, string oldPassword, string newPassword);
} }
public class AccountMembershipService : IMembershipService { public class AccountMembershipService : IMembershipServiceShim {
private readonly MembershipProvider _provider; private readonly MembershipProvider _provider;
public AccountMembershipService() public AccountMembershipService()

View File

@@ -8,12 +8,15 @@ namespace Orchard.Models {
public class DefaultModelManager : IModelManager { public class DefaultModelManager : IModelManager {
private readonly IEnumerable<IModelDriver> _drivers; private readonly IEnumerable<IModelDriver> _drivers;
private readonly IRepository<ModelRecord> _modelRepository; private readonly IRepository<ModelRecord> _modelRepository;
private readonly IRepository<ModelTypeRecord> _modelTypeRepository;
public DefaultModelManager( public DefaultModelManager(
IEnumerable<IModelDriver> drivers, IEnumerable<IModelDriver> drivers,
IRepository<ModelRecord> modelRepository) { IRepository<ModelRecord> modelRepository,
IRepository<ModelTypeRecord> modelTypeRepository) {
_drivers = drivers; _drivers = drivers;
_modelRepository = modelRepository; _modelRepository = modelRepository;
_modelTypeRepository = modelTypeRepository;
} }
public virtual IModel New(string modelType) { public virtual IModel New(string modelType) {
@@ -47,12 +50,44 @@ namespace Orchard.Models {
// set the id // set the id
context.Instance.As<ModelRoot>().Id = context.Id; context.Instance.As<ModelRoot>().Id = context.Id;
// invoke drivers to weld aspects onto kernel // invoke drivers to acquire state, or at least establish lazy loading callbacks
foreach (var driver in _drivers) { foreach (var driver in _drivers) {
driver.Load(context); driver.Load(context);
} }
return context.Instance; return context.Instance;
} }
public void Create(IModel model) {
// produce root record to determine the model id
var modelRecord = new ModelRecord {ModelType = AcquireModelTypeRecord(model.ModelType)};
_modelRepository.Create(modelRecord);
// build a context with the initialized instance to create
var context = new CreateModelContext {
Id = modelRecord.Id,
ModelType = modelRecord.ModelType.Name,
Instance = model.As<ModelRoot>().Welded
};
// set the id
context.Instance.As<ModelRoot>().Id = context.Id;
// invoke drivers to add information to persistent stores
foreach (var driver in _drivers) {
driver.Create(context);
}
}
private ModelTypeRecord AcquireModelTypeRecord(string modelType) {
var modelTypeRecord = _modelTypeRepository.Get(x => x.Name == modelType);
if (modelTypeRecord == null) {
//TEMP: this is not safe... Model types could be created concurrently?
modelTypeRecord = new ModelTypeRecord {Name = modelType};
_modelTypeRepository.Create(modelTypeRecord);
}
return modelTypeRecord;
}
} }
} }

View File

@@ -1,6 +1,7 @@
namespace Orchard.Models.Driver { namespace Orchard.Models.Driver {
public interface IModelDriver : IDependency { public interface IModelDriver : IDependency {
void New(NewModelContext context); void New(NewModelContext context);
void Create(CreateModelContext context);
void Load(LoadModelContext context); void Load(LoadModelContext context);
} }
} }

View File

@@ -1,3 +1,4 @@
using System;
using Orchard.Data; using Orchard.Data;
using Orchard.Logging; using Orchard.Logging;
@@ -10,6 +11,7 @@ namespace Orchard.Models.Driver {
public ILogger Logger{ get; set;} public ILogger Logger{ get; set;}
void IModelDriver.New(NewModelContext context) {New(context);} void IModelDriver.New(NewModelContext context) {New(context);}
void IModelDriver.Create(CreateModelContext context) { Create(context); }
void IModelDriver.Load(LoadModelContext context) { Load(context); } void IModelDriver.Load(LoadModelContext context) { Load(context); }
protected virtual void New(NewModelContext context) { protected virtual void New(NewModelContext context) {
@@ -18,6 +20,9 @@ namespace Orchard.Models.Driver {
protected virtual void Load(LoadModelContext context) { protected virtual void Load(LoadModelContext context) {
} }
protected virtual void Create(CreateModelContext context) {
}
protected void WeldModelPart<TPart>(NewModelContext context) where TPart : class,IModel,new() { protected void WeldModelPart<TPart>(NewModelContext context) where TPart : class,IModel,new() {
var newPart = new TPart(); var newPart = new TPart();
newPart.Weld(context.Instance); newPart.Weld(context.Instance);

View File

@@ -8,4 +8,9 @@ namespace Orchard.Models.Driver {
public string ModelType { get; set; } public string ModelType { get; set; }
public IModel Instance { get; set; } public IModel Instance { get; set; }
} }
public class CreateModelContext {
public int Id { get; set; }
public string ModelType { get; set; }
public IModel Instance { get; set; }
}
} }

View File

@@ -1,6 +1,9 @@
using Orchard.Security;
namespace Orchard.Models { namespace Orchard.Models {
public interface IModelManager { public interface IModelManager {
IModel New(string modelType); IModel New(string modelType);
IModel Get(int id); IModel Get(int id);
void Create(IModel model);
} }
} }

View File

@@ -161,6 +161,7 @@
<Compile Include="Packages\PackageFolders.cs" /> <Compile Include="Packages\PackageFolders.cs" />
<Compile Include="Packages\PackageDescriptor.cs" /> <Compile Include="Packages\PackageDescriptor.cs" />
<Compile Include="Packages\PackageManager.cs" /> <Compile Include="Packages\PackageManager.cs" />
<Compile Include="Security\CreateUserParams.cs" />
<Compile Include="Security\IAuthorizationService.cs" /> <Compile Include="Security\IAuthorizationService.cs" />
<Compile Include="Security\IMembershipService.cs" /> <Compile Include="Security\IMembershipService.cs" />
<Compile Include="Security\IUser.cs" /> <Compile Include="Security\IUser.cs" />

View File

@@ -0,0 +1,43 @@
namespace Orchard.Security {
public class CreateUserParams {
private readonly string _username;
private readonly string _password;
private readonly string _email;
private readonly string _passwordQuestion;
private readonly string _passwordAnswer;
private readonly bool _isApproved;
public CreateUserParams(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved) {
_username = username;
_password = password;
_email = email;
_passwordQuestion = passwordQuestion;
_passwordAnswer = passwordAnswer;
_isApproved = isApproved;
}
public string Username {
get { return _username; }
}
public string Password {
get { return _password; }
}
public string Email {
get { return _email; }
}
public string PasswordQuestion {
get { return _passwordQuestion; }
}
public string PasswordAnswer {
get { return _passwordAnswer; }
}
public bool IsApproved {
get { return _isApproved; }
}
}
}

View File

@@ -7,6 +7,9 @@ using System.Web.Security;
namespace Orchard.Security { namespace Orchard.Security {
public interface IMembershipService : IDependency { public interface IMembershipService : IDependency {
void ReadSettings(MembershipSettings settings); void ReadSettings(MembershipSettings settings);
IUser CreateUser(CreateUserParams createUserParams);
IUser GetUser(string username);
} }
public class MembershipSettings { public class MembershipSettings {

View File

@@ -8,9 +8,6 @@ using Orchard.Environment;
namespace Orchard.Security.Providers { namespace Orchard.Security.Providers {
public class OrchardMembershipProvider : MembershipProvider { public class OrchardMembershipProvider : MembershipProvider {
public OrchardMembershipProvider() {
int x =5;
}
static IMembershipService GetService() { static IMembershipService GetService() {
return ServiceLocator.Resolve<IMembershipService>(); return ServiceLocator.Resolve<IMembershipService>();
@@ -33,11 +30,35 @@ namespace Orchard.Security.Providers {
return settings; return settings;
} }
private MembershipUser BuildMembershipUser(IUser user) {
return new MembershipUser(Name,
user.UserName,
user.Id,
user.Email,
null,
null,
true,
false,
DateTime.UtcNow,
DateTime.UtcNow,
DateTime.UtcNow,
DateTime.UtcNow,
DateTime.UtcNow);
}
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) {
throw new NotImplementedException(); var user = GetService().CreateUser(new CreateUserParams(username, password, email, passwordQuestion, passwordAnswer, isApproved));
if (user == null) {
status = MembershipCreateStatus.ProviderError;
return null;
} }
status = MembershipCreateStatus.Success;
return BuildMembershipUser(user);
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) {
throw new NotImplementedException(); throw new NotImplementedException();
} }