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>
</PropertyGroup>
<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.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</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">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
@@ -46,9 +75,21 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Users\Services\MembershipServiceTests.cs" />
</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>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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) {
var modelRepository = _container.Resolve<IRepository<ModelRecord>>();

View File

@@ -21,7 +21,7 @@ namespace Orchard.Tests.Mvc {
public void Init() {
var builder = new ContainerBuilder();
builder.Register<ReplacementFooController>().As<IController>()
.Named("controller.orchard.web.foo")
.Named("controller.orchard.foo")
.FactoryScoped();
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>
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Models\UserModel.cs" />
<Compile Include="Models\UserDriver.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\MembershipService.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Package.txt" />
<Content Include="Web.config" />
<Content Include="Views\Web.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
<Folder Include="Content\" />
<Folder Include="Models\" />
<Folder Include="Scripts\" />
</ItemGroup>
<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
// of unit testing this type. See the comments at the end of this file for more
// information.
public AccountController(IFormsAuthentication formsAuth, IMembershipService service) {
public AccountController(IFormsAuthentication formsAuth, IMembershipServiceShim service) {
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
}
public IFormsAuthentication FormsAuth { get; private set; }
public IMembershipService MembershipService { get; private set; }
public IMembershipServiceShim MembershipService { get; private set; }
public ActionResult LogOn() {
return View();
@@ -238,7 +238,7 @@ namespace Orchard.Controllers {
#endregion
}
public interface IMembershipService {
public interface IMembershipServiceShim {
int MinPasswordLength { get; }
bool ValidateUser(string userName, string password);
@@ -246,7 +246,7 @@ namespace Orchard.Controllers {
bool ChangePassword(string userName, string oldPassword, string newPassword);
}
public class AccountMembershipService : IMembershipService {
public class AccountMembershipService : IMembershipServiceShim {
private readonly MembershipProvider _provider;
public AccountMembershipService()

View File

@@ -8,12 +8,15 @@ namespace Orchard.Models {
public class DefaultModelManager : IModelManager {
private readonly IEnumerable<IModelDriver> _drivers;
private readonly IRepository<ModelRecord> _modelRepository;
private readonly IRepository<ModelTypeRecord> _modelTypeRepository;
public DefaultModelManager(
IEnumerable<IModelDriver> drivers,
IRepository<ModelRecord> modelRepository) {
IRepository<ModelRecord> modelRepository,
IRepository<ModelTypeRecord> modelTypeRepository) {
_drivers = drivers;
_modelRepository = modelRepository;
_modelTypeRepository = modelTypeRepository;
}
public virtual IModel New(string modelType) {
@@ -47,12 +50,44 @@ namespace Orchard.Models {
// set the 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) {
driver.Load(context);
}
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 {
public interface IModelDriver : IDependency {
void New(NewModelContext context);
void Create(CreateModelContext context);
void Load(LoadModelContext context);
}
}

View File

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

View File

@@ -8,4 +8,9 @@ namespace Orchard.Models.Driver {
public string ModelType { 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 {
public interface IModelManager {
IModel New(string modelType);
IModel Get(int id);
void Create(IModel model);
}
}

View File

@@ -161,6 +161,7 @@
<Compile Include="Packages\PackageFolders.cs" />
<Compile Include="Packages\PackageDescriptor.cs" />
<Compile Include="Packages\PackageManager.cs" />
<Compile Include="Security\CreateUserParams.cs" />
<Compile Include="Security\IAuthorizationService.cs" />
<Compile Include="Security\IMembershipService.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 {
public interface IMembershipService : IDependency {
void ReadSettings(MembershipSettings settings);
IUser CreateUser(CreateUserParams createUserParams);
IUser GetUser(string username);
}
public class MembershipSettings {

View File

@@ -8,9 +8,6 @@ using Orchard.Environment;
namespace Orchard.Security.Providers {
public class OrchardMembershipProvider : MembershipProvider {
public OrchardMembershipProvider() {
int x =5;
}
static IMembershipService GetService() {
return ServiceLocator.Resolve<IMembershipService>();
@@ -33,11 +30,35 @@ namespace Orchard.Security.Providers {
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) {
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) {
throw new NotImplementedException();
}