views:

67

answers:

2

I have a page using an injected BLL service: a simple service returning a set of objects with a function like this:

public IMyService { List<Foo> All(); }

There is a default implementation for normal users. Now, i need that users in administrative role can view more objects, with another implementation of the service.

Where can i configure my page to use the second implementation?

My first solution is to put the dependency to the IUnityContainer in the page, and use it to resolve the dependency:

[Dependency]
public IUnityContainer Container { get; set;}

Page_Init(..) 
{ 
    _myService = User.IsInRole(MyRoles.Administrators)
                 ? Container.Resolve<IMyService>("forAdmins")
                 : Container.Resolve<IMyService>();
}

But it's very ugly: it's a ServiceLocator and it's neither scalable neither testable.

How can i handle this situation? Maybe creating a child container for every role?

+1  A: 

I agree with you that your current design is ugly. What I personally dislike about this approach is that you are setting up the security configuration inside a page. You will have a security bug when anyone forgets this and how are you testing that this page configuration is correct?

Here are two ideas: First: Use a factory that is able to resolve the correct implementation of that service based on the user roles:

public static class MyServiceFactory
{
    public static IMyService GetServiceForCurrentUser()
    {
        var highestRoleForUser = GetHighestRoleForUser();

        Container.Resolve<IMyService>(highestRoleForUser);
    }

    private static string GetHighestRoleForUser()
    {
        var roles = Roles.GetRolesForUser().ToList();
        roles.Sort();
        return roles.Last();
    }
}

Second: Have multiple methods on that interface, one for normal users, one for administrators. The implementation of that interface can have the PrincipalPermissionAttribute defined on the restricted methods:

class MyServiceImpl : IMyService
{
    public List<Foo> All()
    {
       // TODO
    }

    [PrincipalPermission(SecurityAction.Demand, Role ="Administrator")]
    public List<Foo> AllAdmin()
    {
       // TODO
    }
}

I hope this helps.

Steven
+4  A: 

You could implement it as a combination of Decorator and Composite:

public SelectiveService : IMyService
{
    private readonly IMyService normalService;
    private readonly IMyService adminService;

    public SelectiveService(IMyService normalService, IMyService adminService)
    {
        if (normalService == null)
        {
            throw new ArgumentNullException("normalService");
        }
        if (adminService == null)
        {
            throw new ArgumentNullException("adminService");
        }

        this.normalService = normalService;
        this.adminService = adminService;
    }

    public List<Foo> All()
    {
        if(Thread.CurrentPrincipal.IsInRole(MyRoles.Administrators))
        {
            return this.adminService.All();
        }
        return this.normalService.All();
    }
}

This follows the Single Responsibility Principle since each implementation does only one thing.

Mark Seemann
As always, nice! +1
Steven