User / Role Admin screens do not use services

Topics: Core
Oct 24, 2012 at 5:30 PM

Hi Folks,

anyone who has seen many of my posts might be aware that we have been replacing the authentication/membership system for Orchard, plugging in a pre-existing one from a client. In doing this we implemented Orchards IMembershipService among other things. However, when I look at the User administration controller (v1.5.1), I see that it does not use a service to get the list of users etc, but instead goes straight to UserParts (an Orchard only representation).

Is this something that is on the radar for change/something the team are generally happy with/etc? I'm quite interested, because most of the parts of Orchard are extremely well driven by interfaces, which can be swapped at will, and in general the Membership stuff does not appear to follow this so completely.

Having stepped through many of the parts of Users/Roles/Membership/Authentication etc, I would consider doing the work of revising this (to only call "Parts" within specific implementations) if that is something that was valuable to the project (I've not checked the changes in 1.6 changes yet). In the same vein this would include looking at the fact that the IUser interface inherits from IContent, which is not so good if your implementation of users is not stored in Orchard

Let me know if this is of interest/already underway/not of interest

cheers,

Developer
Oct 25, 2012 at 5:48 AM
Edited Oct 25, 2012 at 5:50 AM

Not gonna happen in the nearest future, I'm pretty sure about that.

"User" is a content type in Orchard by design and needs to be stored in the database. This way you may easily query users and reference them within other records by using an ID, like anything else.

I agree that it adds a bit of overhead when trying to externalize the user storage. The easiest way to do that is to keep a mapping between Orchard and external users (by using a custom part attached to user). It works pretty well in most scenarios. So at least one user has to exist in Orchard database - you may override the whole authorization and authentication and in the end eg. map all your users to single Orchard user, if you don't want to keep 1-1 relation between Orchard and external users.

On the other hand, it's very hard but not impossible to get rid of Orchard.Users module and fully replace it with your own implementation. Implementing IContent would be the hard part and it will most probably break all modules that use IContentManager to get users by Id (or perform queries for users), because those would always return nothing. And you'd need to ensure the generated Id won't conflict with the ones existing with the Orchard database, etc. Can of worms...

Oct 25, 2012 at 8:47 AM
Edited Oct 25, 2012 at 8:49 AM

We've already achieved no user storage in the database - we fake IUser objects from the real users, but yeah - a lot of stuff in the back end makes use of IContent. We've been told specifically that users must not appear in the Orchard database for this project, so I'll need to work it out even if it's not contributed back. The frustration is of course that 50% of code uses IMembershipService, and 50% doesn't.

I think the main issue to any patch to Orchard is I can't see a way to do it without causing a massive breaking change (based on all those modules and admin screens which have been using IUSer and direct access to UserParts for so long).

Developer
Oct 25, 2012 at 3:51 PM
Edited Oct 25, 2012 at 3:52 PM

The IMembershipService doesn't allow you to do complex queries over user store, so in lots of cases it is not usable enough... You may try having a single user in the database (the default admin, created during setup) and provide your own module that would

  • override the Orchard.Users functionality (eg. override admin routes, so user management screens won't be accessible)
  • provide custom IMembershipService provider
  • provide custom IAuthorizationService provider

This module should also override the default implementation of IUser (UserPart) and provide your own CustomUserPart without database backed storage (inheriting from ContentPart, not ContentPart<TRecord>), so you would be able to fill it with appropriate data on the fly.

Nov 1, 2012 at 11:34 AM

Hi pszmyd we had already done all of the above :) Out main difficulties were that IUser is a bit of a leaky interface, and exposes ContentItem (to my mind it should IUser should effectively be a POCO). Populating UserPart and all its other bits correctly is complex, when you consider roles etc. If IMembershipService/IUser had hidden the implementation properly, replacing authentication would have been a doddle. User "by design" could have had an implementation that stored in the Orchard database (which could have done it however it liked ) while still providing a neutral interface to program the rest of the UI against. 

Nov 13, 2012 at 10:35 PM

Hi, please give me advice how to change user column name for password and login ?  I have mysql database with user data but can't change this fields (in use by another app).

Should I write new module and rewrite some services? Please give me some hints. Thanks in advance.

P.S. I'm newbie in orchard )

Developer
Nov 14, 2012 at 12:34 AM

@hunterex It's not that simple. NHibernate maps column names to appropriate properties on the record object, so they need to be the same. It's by convention. If you don't mind altering the core, you may

  • rename the appropriate columns in <tenant>_Orchard_Users_UserPartRecord table and 
  • rename the properties of UserPartRecord class (only the record - leave the property names on UserPart class unchanged if you don't want to get into trouble!) - after the change you may need to fix the names of those properties across Orchard.Users module too (if VS/R# won't do that automatically).
  • remove /AppData/Sites/<tenant>/mappings.bin file
  • restart the application
Nov 14, 2012 at 12:30 PM

Thank you Piotr for reply! I also find some info here: http://orchard.codeplex.com/discussions/244943 as you wrote:

pszmyd wrote:
This depends on what you really want to achieve. If you'd like only to check username/password then yes (no custom authentication scheme - single-sign-on and such) - implementing custom IMembershipService is the only thing you might want. You can inherit the default MembershipService and override only the methods you mentioned (GetUser and ValidateUser), but that would mean that you don't want to create new users (CreateUser method) in your existing DB and don't want users to change their passwords (ChangePassword method)... If you want both of those functionalities too, you should just implement the IMembershipInterface in whole. About DI - yes, by default, objects created inside custom modules have higher priority over the core ones and will become the default implementations injected by IoC container. So there's nothing more for you to do. Of course the old object is still accessible if you need that - you can get it's instance by injecting IEnumerable - this will get you all existing interface implementations (old and new ones).

 

So I decided try to make some module which will override MembershipService methods with mysql query's