Which ContentHandler to override before a content item is displayed?

Sep 30, 2010 at 6:29 PM

Hi

I'm writing a module with a content part that you can add to any content item. The content part has an editor view that presents a list of roles so you can select which are allowed to view this content item, the default being the Anonymous role

That was the easy bit but which is the best placed ContentHandler function where I can test that the current user has permission to view the content item?

And when the user doesn't have permission to view that content item do you have any advice about how I can stop the content item being displayed and displaying a custom view instead?

Thanks

Gareth

Coordinator
Sep 30, 2010 at 10:14 PM

A handler is not the best place to do that. There actually is an extensibility point in the role based authorization service. You can implement IAuthorizationServiceEventHandler and implement the Checking method. You will get a CheckAccessContext object that will enable you to look at what permission is being checked, for what user and what piece of content. Set Granted to false if the information you stored in your part does not check. Let me know how that goes.

Oct 2, 2010 at 2:21 PM
Edited Oct 2, 2010 at 2:21 PM

Thanks Bertrand I've had a couple of hours on this today and couldn't get it to work because :

1: Checking() is too early to set Granted = false?

I had to put Granted=false in the Complete() function not the Checking() function because there is some code in TryCheckAccess() after it calls .Checking() that always overrides whatever I set Granted to. I setup another user that wasn't an administrator because the code in TryCheckAccess() seems to give super users special permissions but Granted was still reset to true after Checking()

2: IAuthorizationServiceEventHandler only applies to the admin pages?

This one is the real problem with what I'm trying to do. From what I understood of the original request for this functionality at http://social.msdn.microsoft.com/Forums/en-US/orcharddiscussions/thread/b2748504-bef8-4628-9a2d-ce6487e89a9c  we want this to work on the public site (not the admin pages) so we can block anonymous users from viewing certain content items. From my testing it looks like IAuthorizationServiceEventHandler works for the admin pages only?

Oct 2, 2010 at 2:24 PM

I was also wondering how modules would compete over who gets to set Granted, seems like the last to be called wins? Guessing this is a perennial problem with events. Is there some kind of priority I can give that other modules handling the same event can be aware of when deciding whether to set Granted?

Coordinator
Oct 4, 2010 at 3:57 AM

(1) Checking()

Yep - Checking is more of an informational event to let you know the process is about to start. You wouldn't be able to define the answer there, but you could do some substitutions. Doesn't apply to your case but in Checking you could the user, permission, or content values before the access test begins.

Complete does work if you want to alter the pass/fail Granted value. You can also use the Adjust event to change the Granted boolean to false - be sure to leave the Adjusted boolean to false, though, because if you set Adjusted to true you'll be asking the roles module to recalculate based on your changes.

(2) frontend authorization

That is correct at the moment. There is not a frontend access check in place at the points you would need to control - those would be:

Orchard.Core.Contents.Controllers.ItemController Display(int id)

Orchard.Core.Routable.Controllers.ItemController Display(string path)

Assuming some view access checks were in place there you'd still be redirected to an access denied or logon page rather than a custom view.

Interestingly those both call IContentManager.BuildDisplayModel... Maybe a handler on those events would be a better place to replace the shape after all?

(3) module/event priority/order

yeah, it is one of those nature-of-the-beast situations. something we should double check is what order the services come back in when resolved from the IoC container. e.g. is it deterministic? determined by what? it would be nice if the modules or features could have a priority, and also take into account dependency order, so you can make assumptions about order-of-execution between modules handling events...

 

Oct 4, 2010 at 11:36 AM

Thanks Lou

1) Ok

2) Yes that's right it doesn't need to be an access hook specifically. Generically speaking I just need to handle an event that gets the opportunity to stop a content item being displayed and redirect to a view or a different url. If I can't do this in a ContentHandler then I would need to handle a lower level event and check for the presence of the RolePermission content part in the content item, and then run my access check code. Maybe DuildDisplayModel() allows me to do this I'll have a look. You mention replacing the shape so perhaps you see a way of doing this in 0.8 rather than 0.5?

3) Perhaps one simple (and probably obvious) option would be for a handler to be able to say "stop, that's it" and prevent further handlers being triggered for that event if the current handler's priority is higher than any others left in the queue. If there are higher priority handlers left in the queue then only those would run if a previous handler has tried to force the event handling to stop and lower priority handlers would be ignored. Seems messy and confusing to work with though

Coordinator
Oct 4, 2010 at 3:00 PM

Aas Louis said you can detect a Content Item is read by hooking on the BuildDisplayModel event, and checking for the "Display" requested view in the context. Dave is using this technique to make a custom module counting page displays per content type. And even if there are other views called, like a "Summary" you could still handle it here. You could try to throw a dedicated exception to see if it's working.

Oct 5, 2010 at 10:55 PM

I have a similar need, where I need to apply per-department restrictions to content. Some content for example might be limited to the HR department only, anyone else would get a "You don't have permission to view this content" message.

I was also going to write a part that could be added to content that allowed picking "All" or a specific department, but wasn't sure on the best place to choose which view was displayed.

I looked around for an example of how the BuildDisplayModel event could be used, but the only place I could find that gave any hints was the Orchard.DevTools.Handlers.DebugLinkHandler that is overriding the BuildDisplayModel method.

If this is the correct place to do a content-based security check, is there any chance of a code snippet or some guidance on how this could be done? I'm also not sure how to change the output to the "no permission" view when the user fails the check.