For recent project I have to implement forms based authentication for SQL Server 2005 Reporting services. A great starting point is
‘Using SQL Reporting Services 2005 and Forms Authentication with the Whidbey/2.0 SQLMembershipProvider’ by Russell Christopher
http://blogs.msdn.com/bimusings/archive/2005/12/05/500195.aspx
It took me a while to get it all working but I succeeded in the end. The next problem was that this solution allows users who are logged on through the ASP.NET SQLMembershipProvider to be mapped to SQL reporting services users, but it doesn’t support the RoleManager in asp.net 2.0. If you have hundreds of users you would need to set security for each one them within Reporting services, not very practical. Instead I would like to assign them to asp.net roles and then set permissions for these roles within Reporting Services. Even though RS supports Windows Groups when using Windows Authentication I couldn’t figure out a supported way to do this with ASP.NET roles.
I first looked into the RS users, Policies and SecData tables but it didn’t help. However I could see that Windows groups are just another type of user in the users table. My idea was to try to get ASP.NET role names into this table and then pass in the name of the roles rather the name of the user when authenticating or authorizing a user.
The plan is to assign ASP.NET users to exactly one ASP.NET role and then use the name of that role rather the name of the user for reporting services.
I only needed four fixed roles for my application, so I started creating four dummy users ‘rsGuests, rsStandard, rsPremium, rsAdmins’ as ASP.NET users through the “ASP.NET Web Site Administration Tool”.
Over in Reporting Services I logged on as an admin user through forms authentication and in the properties of the home section I added four ‘new role assignments’, one for each user I just created. This way I got my four users into the RS database. I assigned all RS roles to the ‘rsAdmins’ user. This ensures that at least one user has admin access.
Back in the ‘Web Site Administration Tool’ I created four new roles with the same names as the four users. Then I added a bunch of real users to these roles.
So much for the preparation, the real change lies in the AuthenticationExtension class which is part of the Custom Security sample of Reporting Services. The key method here is GetUserInfo which is used by Reporting Services to get the identity of the currently logged on user. In the sample code the userIdentity returned as the HttpContext.Current.User.Identity, the Name Property of this class has the username which is used to look up permissions in RS.
I created a new class RSIdentity which I will use instead:
class RSIdentity: System.Security.Principal.IIdentity { string _userName = string.Empty; public RSIdentity(string userName) { _userName = userName; } public string AuthenticationType { get { return "Forms"; } } public bool IsAuthenticated { get { return true; } } public string Name { get { return _userName; } } }
And then changed to code in GetUserInfo as follows:
public void GetUserInfo(out IIdentity userIdentity, out IntPtr userId) { if (HttpContext.Current != null && HttpContext.Current.User != null) { string[] rolesArray; rolesArray = Roles.GetRolesForUser(); if (rolesArray.Length == 0) { userIdentity = null; } else { userIdentity = new RSIdentity(rolesArray[0]); } } else userIdentity = null; // initialize a pointer to the current user id to zero userId = IntPtr.Zero; }
Here I am getting the first (and only) role the current user belongs to and pass that into my new RSIndentity class which is using it for it’s name property.
Now, when Reporting services is using GetUserInfo it gets the ‘identity’ for the role rather than the user.