Rights and Roles

Topics: Business Logic Layer
Oct 23, 2010 at 1:42 AM

Is there a current plan for how custom roles and rights are going to be implemented into BlogEngine?

Coordinator
Oct 23, 2010 at 2:33 AM

I'm not aware of a plan, although Ruslan may have one.  At this point, a person could extend or customize BE by giving certain users rights based on what role their in.  We've facilitated that by building the UI to create roles and assign them to users.  If you have something in mind, we can certainly look into that -- I'm sure it would probably be better than what we're doing now (which isn't much).

Oct 23, 2010 at 2:51 AM

Well, right now I'm playing around with using a flag Enum called "Rights" that has a bunch of values in it(like CanEditPosts, CanCreatePosts, CanApproveComments, CanCreateRoles etc). Then I'm having various places call a method called IsAuthorizedTo which you pass one of the Rights to. That method then checks to see if the current user object has a role that allows that Right and return true/false. I haven't implemented the actual role checking yet, though.

Obviously, this method doesn't work if BlogEngine were to include a way to create dynamic rights. I'm not even sure how that could actually be implemented.

I like this method better than the current checking of IsAuthenticated/IsInRole(Administrator). In the code it makes it very clear what's required for authorization and what's not.

I also tried playing around with making custom a CodeAccessSecurityAttribute, but I couldn't figure out how to make that work cleanly.

 

Coordinator
Oct 23, 2010 at 7:05 AM

It sounds like the enum and the IsAuthorizedTo methods is sufficient (I personally like using enums btw, so happy to hear about that).

As long as there's capability to check for Rights to do things within BE, that should be good.  We don't need to necessarily have a system that offers enough dynamism (?) for how BE users might like to modify or extend it.  It sounds like someone who might want to modify BE, and add a Right to do something could probably add their Right to the existing enum and modify IsAuthorizedTo.  Or their component/widget may do its own check to see if the user is in a certain role.  I think we're good here since BE has the UI to allow someone to easily assign people to roles.

CodeAccessSecurityAttribute sounds like it is tied to the CAS model (medium trust, high trust, etc).  That sounds a bit like it's not the best fit for BE.  But one option, if ever needed, would be to create our own attribute (like how we currently have the Extension Attribute for extensions).  Security could be defined by decorating methods with this attribute.  But this may not be necessary or useful ... and it sounds like what you have now is a good direction.

Coordinator
Oct 23, 2010 at 3:07 PM

It's on todo list :) As you can see in the latest builds, we have "rights" placeholder in the users section. The idea is to let blogger to manage what functions users have access to within a blog, not to manage functions itself. So enums should do just fine because new function do not need to be dynamic. If you got ideas and want to help, send me email and we can coordinate this.

Coordinator
Oct 23, 2010 at 4:34 PM

Oops .. yes I do remember seeing Rights and the placeholder !

Oct 23, 2010 at 4:44 PM

haha, I saw that placeholder. I was just curious if there was an implementation planned for it yet. I'm gonna clean up the enum/IsAuthorizedTo idea sometime next week and then I'll shoot out an email about it with some code.

Oct 24, 2010 at 1:27 AM

Might be worthwhile converting to a claims based security model as it is much more flexible and extensible. I've seen a few places where people have wanted integration with other security stores (Windows, AD or an STS for example). WIF (http://msdn.microsoft.com/en-us/security/aa570351.aspx) makes that really easy. Using an STS also has the advantage of supporting several ways of authenticating to the same application. It is also just worthwhile abstracting the security concerns from the application code base.

Oct 27, 2010 at 1:37 AM

I've uploaded a bit of a proof-of-concept for my idea at http://blogengine.codeplex.com/SourceControl/network/Forks/rossisdead/SecurityRights

The basic idea is this(but it's not fully implemented, explained later):

-The new Security class gives a static, unified resource for BlogEngine to detect if a user is authenticated and authorized to do something.
- The Security class creates an instance of a BaseSecurityProvider derived class that does the actual meat of the work. This is so anyone can plug in their own security provider without having to recode the whole engine.
- The Security/BaseSecurityProvider classes provide an IsAuthorizedTo method that accepts an enum that represents one right/permission a user role has. This enum has a one-to-one relation with a matching Right object.
- The Role class is extended to include working with a collection of Rights it has.
- The BaseSecurityProvider classes provide a method for enumerating all the roles a user is in, then combines all the Rights into one collection.

You can test it in action. I've updated the User controls/CommentView class to use the new methods provided. I also updated Roles.xml and the XmlRoleProvider to parse the new rights node upon load(it doesn't do anything with saving or anything else though).

 

There's some shortcomings in BlogEngine that make this difficult to implement fully at the moment. Specifically, the Role class is only used internally by the XmlRoleProvider. The DbRoleProvider ignores it entirely. It'd be great if the Role class could be made into an externally visible class that all RoleProviders use.

 

Coordinator
Oct 27, 2010 at 6:51 PM

Looks like a good start.  A few comments......

(1) The last thing you said about "shortcomings" looks related to your comment in DefaultSecurityProvider, for the CurrentUserRights check.  Regardless of which role provider is being used (XML or DB), you can get the roles a user is in via:

string[] userRoles = System.Web.Security.Roles.GetRolesForUser(CurrentUser.Identity.Name);

Unless I misinterpreted the goal, that will use the default role provider to get an array of roles the user is in.

(2) What about anonymous users?  For example, if I'm not logged in and the "require login to post comment" setting isn't enabled (meaning anonymous users can post comments), right now IsAuthorizedTo returns false for the "CreateComments" enum value.

(3) Just curious if you have anything in mind for how a right like "EditOwnPosts" will be checked.  Will the calling code check to see if the user is the post author, and then call IsAuthoriedTo(RightFlags.EditOwnPosts) ?  Or maybe this security check might be put into the Post class that would do the first check (to see if the user is the post author), and then if true, call IsAuthoriedTo(RightFlags.EditOwnPosts), and if the user is not the post author, it would instead call IsAuthoriedTo(RightFlags.EditOtherUsersPosts) ?

(4) It doesn't look like the base RoleProvider class has the concept of "Rights" tied to a role.  Because people use their own Role providers (not our XML or DB role provider), I'm thinking that the process to load/save/store Rights will need to be moved outside the XmlRoleProvider.  For example, some people use the default ASP.NET SQL membership/role providers.  It seems like the "Right" class could instead handle this job of managing the Rights for each role.  The Xml providers and Db providers in BE would still need to each be responsible for loading/saving the rights for each role, but this would be outside the XmlRoleProvider and DbRoleProvider.  For the DB, we would probably create a new DB table (be_RoleRights), and for the XML storage, that would probably be best to store in a separate XML file (not roles.xml).  If we try to store it in the same roles.xml file, the Role provider will probably overwrite it -- losing the Rights stored with each role.

It's looking good though.  Please take these comments above as helpful (or unhelpful!) suggestions.

Oct 27, 2010 at 7:22 PM
Hey Ben, thanks for looking it over! I appreciate it. Let me address your points. 1. I realize there's that method that returns a string array for getting the roles a user's in. I was looking more for a way to get a Role class that could contain a bit more information in it. I suppose I could make my own, I just saw that the XmlRoleProvider uses its own Role class(instead of just holding on to role names), so I figured that could be extended a bit more. 2. My idea for situations like that, which I think I left in the code comments somewhere, would be to create an "Anonymous" role. It'd be similar to Administrator in that you can't delete that role. That would give a ton of flexibility to what anonymous users can and can't do. The Anonymous role would have access to the same rights as any other role, and whoever's in charge of editing roles/rights could adjust settings as they see fit. 3. That's pretty much what I had in mind. 4. I didn't think about the fact that some users might wanna use a role provider that doesn't extend from one of the defaults that BlogEngine offers. The whole provider thing is new to me, so I really don't know how to go about implementing that aspect of it correctly. I'm guessing in this instance it would make sense to save/load Rights using the BlogProvider classes?
Coordinator
Oct 27, 2010 at 8:53 PM

A built-in Anonymous role like you suggest sounds like a good idea.  And then rights could be assigned to the Anonymous role.  I realize various parts like this are yet-to-be developed.  Was just curious, sounds good though.

Yes, that looks right about the BlogProvider class.  New methods, etc would be added to BlogProvider to support "Role Rights".  And then implemented in the DbBlogProvider class (this single file implements all BlogProvider methods!), and maybe a new file in the Providers\XmlProvider folder for an XML implementation of Role Rights (the XML files here are all partial classes).

Regarding this whole point, although someone could inherit from the DbRoleProvider or XmlRoleProvider, my guess is that most people who have their own implementations are inheriting from the base RoleProvider class (the same class DbRoleProvider and XmlRoleProvider) inherit from.  A common one is people who are using the built-in ASP.NET membership system.  They can use the standard "System.Web.Security.SqlMembershipProvider" class that's included with ASP.NET.  We even have a documentation page here on switching to that.  It's really just a matter of adding the provider to the web.config file and making it the "default".  In some more rarer cases, some people might be using Windows Active Directory for membership and roles.  There was a discussion here recently where someone was doing that.

Glad we collaborated here!

Oct 28, 2010 at 10:53 PM

I spent yesterday reworking the idea a bit and uploaded the changes to my security fork.

- I removed the BaseSecurityProvider/DefaultSecurityProvider classes because they just seemed redundant and needless since the whole thing has to work with role management anyway.

- I added FillRights() and SaveRights() to the BlogService class and added abstract methods to the BlogProvider class for the same things. I implemented it fully in XmlBlogProvider, but DbBlogProvider will throw NotSupportedExceptions. Someone else would have to create that part.

- I added and implemented default values(if none are found) for the Anonymous role. The default values are CreateComments, ViewPosts, ViewPages, and ViewComments. I think that's equivalent to the initial install of BlogEngine.

- The admin panel has a bare-bones Rights section implemented for basic editing of rights. You can get to them by going to the Roles page and clicking the "Edit Rights" link on any role.  The whole thing works, it just needs to be made pretty. I opted not to do that myself only because I know someone else is working on the admin panel redesign.

- I updated RoleService to be a bit better at catching errors and also make use of the Security.IsAuthorizedTo method.

 

I think that this is setting up to be a pretty solid foundation for an easy-to-use security/rights system. I'm starting a new job soon, though, so I'm not sure how much time I'll be able to spend on perfecting this system. I'm hoping someone can pick up where I left off at some point.

 

Oct 29, 2010 at 2:05 AM

I also just wanted to add this quick mental note. The more I work with the roles, the more I realize that there should be a static role management class similar to the static BlogServices class. At the moment, BlogEngine specific rules(like creating the Administrator role, and now the Anonymous role) are added by the custom providers. This wouldn't create those roles for people using alternate role providers.

Coordinator
Oct 30, 2010 at 9:45 PM

Hey, thanks for the all the work up till this point.  I just merged your latest fork into BE (1.6.2.29).

I implemented FillRights() and SaveRights() in the DbBlogProvider.  I see what you mean about having a BE Role manager that would act as a wrapper around the normal Role provider.  This is a good idea.  What I did though for this checkin, is I moved the code in the XmlRoleProvider that was adding the Administrator and Anonymous roles (if they didn't exist).  I moved this code to the constructor of the Right class.  By having it there, it handles those checks regardless of which role provider is being used.  So in a way, the Right class is acting as that static Role manager.  But this is not as ideal as having a dedicated Role manager.

I added a couple of things including a IsSystemRole() static function in the Security class that returns true if the roleName passed in is Administrator or Anonymous.  IsSystemRole() is then used to make sure that these roles cannot be deleted, for example.  A couple of other separate checks are done in each role provider class to make sure a user cannot be added to the anonymous role.  Along the same lines, on the UI Profiles page (where you assign users into roles), a checkbox for Anonymous user is excluded so you cannot add a user to the Anonymous role.

Nov 4, 2010 at 12:10 AM

I'm glad to see this is being embraced! I can't wait to see where it goes :)

Nov 4, 2010 at 10:18 AM
Edited Nov 4, 2010 at 10:18 AM

Hello,

 

I am taking this train very late but...

I would prefer an role nammed 'AllUsers' than 'Anonymou's which could lead to misunderstanding and error with the anonymous access managed by the MembershiProvider (Cf default MS providers).

I also would add that Roles could be made read only from an appsetting, because in my case, I am woking with blogEngine UNDER another portal and using the providers from this portal, and the portal is the only one to be able to chang roles.

I would appreciate that doors could keeep opened for this.

 

As I have already to struggle with facilities inside /account being useless in my case and not easy to remove....

Same thing for Application_Start but this one is easy to turn around.

 

Best regards

CS

Coordinator
Nov 4, 2010 at 5:10 PM

Thanks for the feedback.

Anonymous doesn't mean All Users.  It means users who are unauthenticated.  All Users would imply that it's either "logged in users" or "everyone" -- both are not correct.   Anonymous is exactly what it means in this case.  I suppose we could use "unauthenticated" instead, but Anonymous is a standard term I think everyone can relate to.

The new Roles page is not required to use.  It's there for people who don't already have another system, and want to create/manage roles within BE.

Nov 4, 2010 at 6:01 PM

Hi Ben,

In fact working with the All users concept in mind bring faster code, no test to do as it works for every one......not necessary to get involved in the MS uneasy way of managing LoginUser / Authenticated users / Thread pool user across IIS 6/7 etc.

Concerning the role Page I see no way to remove it without changing some code ?

 

Best regards

CS

Nov 5, 2010 at 10:37 PM

CS: There's a difference between "Anonymous" and "AllUsers" in the way that it's implemented currently. "AllUsers" would literally indicate every user(authenticated or not), while "Anonymous" only indicates for non-authenticated users. If, say, you required users to login in order to view any posts, you'd want to disable views for users with the Anonymous role, not the AllUsers role.

I do see your point though. An AllUsers/Everyone type role would make a pretty simple spot for setting the defaults that apply to all users, instead of having to set them for each role(which would actually get annoying if you were to have tens of roles and needed to keep setting various settings).

Nov 6, 2010 at 7:26 AM
Edited Nov 6, 2010 at 7:29 AM

The 2.33 code spends lot of time checking for right through many loops and as soon as it enters in OnPreInit, I fear something getting more and more heavy with extensions of this paradigm to everything.

Having a king of 'negative' test with all user could beging a winning pratice: good for all users->not test, user is admin->no test other case tests......

I am not sure extending all these tests to every detail is necessary, what is planned ?

Actually I am using another role provider and I am also fearing the table which contains rights and roles, I will test to day with my own ole provider and I hope that the usage of an external role provider is preserved.

May be energy could have be better used working on the multi-blog paradigm than in making this code complicated with rights before going multiblog.

I don't think it was the good order of priority in the roadmap, see the votes on features.

CS

Coordinator
Nov 6, 2010 at 6:26 PM

CS:  You bring up some good points.  It's true that we've neglected the multiple blog request.  It's a larger project that we couldn't find enough time to organize and execute.  It's still on the roadmap, but won't be making it into BE 2.0.

This new Rights system is also on the roadmap -- indirectly.  It's part of "Better user and role management".  After BE 1.6 was released, and up till now, we've added a lot of things including the ability for self-registration (where anonymous guests and register by themselves), ways to reset your password, allowing roles to be added and managed thru the control panel, and now Rights tied to roles.  Just allowing Roles to be created and assigned was not enough.  The roles wouldn't have any meaning.  With this Rights system, rights can be assigned to the roles, giving meaning to having custom Roles.

The Rights system is also an improvement over what we had before where we inconsistently were checking different ways to see if a person was authorized to do something.  In some cases we just looked to see if the person was authenticated, and in other cases we looking at certain roles.  Even just looking to see if the person was authenticated was done in a couple of different ways that weren't consistent.  Now that self-registration is possible, it's even more important that a more organized, central way to check users rights exists.

Although this isn't as heavily requested of a feature as multiple blogs is, it's still good improvements overall to the BE platform.