MVC – Getting User Roles From Multiple Sources (register and resolve arrays of dependencis using the fluent api)

In the last post I talked about encapsulating the authorisation and user credentials (i.e IPrincipal) logic in a separate, re-usable DLL (known as AuthServices).

Part 1 of that post discussed the DLL contents in detail. Part 2 focused upon using that DLL in an MVC application.

This post follows up on “Part 2” specifically adding a user’s roles to the UserData objet prior to serialisation, on-route to becoming the auth cookie.

The code sample that I gave for using the AuthServices DLL is repeated below:

_authenticationService.Authenticate(userNameString, password);
if (_authenticationService.Result.Authenticated)
{
       _currentUserData = _authenticationService.Result.CurrentUserData;
       _currentUserData.Roles = GetRolesForUser(userName);//THE SUBJECT OF THIS POST
       _cookieManager.CreateUserCookie(userName, _currentUserData);
}

This post explores in more detail what lies behind the GetRolesForUser(string userName) method and how this can utilise more than one provider of roles.

Why bother?

The organisation in which I work is big. We have thousands of staff and many thousands more students. Applications written for an organisation of this size usually need to access different sources of data to provide a user’s roles relevant to the specific application. In projects like this you can’t easily use the standard RoleProvider stuff in a config file.

The Answer – Multiple Role Providers

To implement multiple roles I inject (in an IOC stylee) into my login service the interface IRolesMgr. IRolesMgr exposes the following:

public interface IRolesMgr
    {
        List GetAllRolesForUser(string userName);
    }

The interesting part is in the constructor of the implementation.

public class RolesMgr: IRolesMgr
    {
        private IRoleMgrComponent[] _roleManagers;

        public RolesMgr(IRoleMgrComponent[] roleManagers)
        {
            _roleManagers = roleManagers;
        }

        public List GetAllRolesForUser(string userName)
        {
            List roles = new List();
            foreach (var mgr in _roleManagers)
            {
                roles.AddRange(mgr.GetRolesForUser(userName));
            }
        }
    }

As can be seen the constructor has a dependency on an array of IRoleMgrComponents each of which will contain the access logic needed to identify a user’s roles from a different source.
Each IRoleMgrComponent implements the following:

public interface IRoleMgrComponent
    {
        List GetRolesForUser(string userName);
    }

That’s all very good, but the trick is getting the IOC container, in this case, Castle Windsor, to instantiate the appropriate implementations of IRoleMgrComponent in the roleManagers array.

This is done in the Component Registrar class used to register components to Castle by using the “ServiceOveride” method (more details of this can be seen on Mike Hadlow’s post – which was the original inspiration for the castle registration code – thanks Mike!). In effect this allows you to register and resolve arrays of dependencis using the fluent api.

container.Register(
                Component.For()
                    .ImplementedBy()
                    .ServiceOverrides(
                        ServiceOverride.ForKey("roleManagers").Eq(
                            "HumsAuthDbRoleManager",
                            "StudentAuthRoleMgr"
                        )
                    ),
                AllTypes
                    .FromAssemblyNamed("ApplicationServices")
                    .BasedOn()
                        .WithService.FromInterface(typeof(IRoleMgrComponent))
                        .Configure(c => c.Named(c.Implementation.Name))
                );

Once you have overriden this specific component you can continue to register all the other components in that assembly in the normal way, say:

container.Register(
                AllTypes.Pick()
                .FromAssemblyNamed("ApplicationServices")
                .WithService.FirstInterface());

Conclusion

Implementing RolesMgr(IRoleMgrComponent[]) in conjunction with the AuthServices assembly talked about in the last post, means that for any given new project I will only have to write minimal code yet benefit from User Credential information that:
a) Has the execution logic encapsulated in an (extensible) AuthServices library
b) Can uitilise dependency injection to configure how where and how the roles data comes from in the credential information.

Advertisements