Credentials handling - MenuServices

Feb 1, 2012 at 5:14 PM

I modified the inner working of the MenuServices property in RunWeb.cs, so I was able to create the menu structure I wanted. Is there any way to change the menu items based on the logged in user's credentials? For example: if the admin is logged on, all items should be visible. If a user with limited permissions is logged on, only some menu items should be visible.

My problem is that the MenuServices property is initialized only once, even before any login screen has been shown. So I cannot selectively add the menu items depending on the credentials of the logged in user. Is there any way to handle this?

Thanks in advance.

Coordinator
Feb 1, 2012 at 5:42 PM
Edited Feb 1, 2012 at 5:46 PM

This should fall out automatically from the authorization mechanism  -  see the manual section Authentication & Authorization.  If the logged on user is not able to view any of the actions on a particular service (that is registered in MenuServices) then that service should not appear in the menu list.

In other words:  there should be no need for you to modify how that property works.  Just mark up all the actions on your services with the NakedObjects.AuthorizeAction attribute, indicating the roles that that action should be available to.

Feb 1, 2012 at 6:06 PM
Hey there.

Yes, I had exactly the same requirement and have quite a nice
implementation working.

It requires you to delegate to some or other security controller
within the HideXXX methods of your repositories and entities that
relate to the menu items that you want to hide or show based on the
users role.

If you have a PersonRepository with a NewPerson action, and a
corresponding HideNewPerson method, it might look like the following:

public bool HideNewPerson ()
{
return !Access.CanEdit ();
// Or
// return !Access.IsInRole (Enums.Role.SysAdmin);
}

I created a class called Access and registered it in SystemServices.
Here is a sample of it:

public class Access : AbstractFactoryAndRepository
{
public Person GetCurrentPerson ()
{
var email = Container.Principal.Identity.Name;
return Container.Instances<Person> ().FindByEmail (email);
}

/// <summary>
/// Returns true if the currently logged in user has permission to
add and edit person details.
/// </summary>
/// <returns></returns>
public bool CanEdit ()
{
return IsInRole (Enums.Role.SysAdmin, Enums.Role.Editor);
}

/// <summary>
/// Returns true if the currently logged in user is in at least one
of the specified roles.
/// </summary>
/// <param groupName="role"></param>
/// <returns></returns>
public bool IsInRole (params Enums.Role[] roles)
{
var person = GetCurrentPerson ();
if (person == null)
return false;

foreach (var role in roles)
{
if (person.Users.SelectMany (u => u.UserRoles).Any (ur =>
ur.RoleId == (byte)role))
return true;
}

return false;
}

}

Code can still be optimised, but works nicely.
Hope that helps.
Feb 1, 2012 at 6:17 PM

I implemented mine before that authorization Richard mentioned was available in Naked Objects MVC.

In adition, I had to handle permissions based on entity state as well as role in some situations, and that is not possible with the attributes approach.

Feb 2, 2012 at 4:14 PM

Thank you, both of you. I used what Richard suggested, it just works fine.