![]() |
|
|
By: Ray Barley | Read Comments (11) | Print Ray is a Principal Architect at RDA Corporation and a MSSQLTips.com BI Expert. Related Tips: More |
|
Problem
I have a number of reports that anyone in the organization can view. These reports are coming from a SQL Server 2005 database. However, there are limitations on the data that a person can access. A person may be able to see all data, data from just their office, or data from a specific list of offices. How can I go about implementing this security scheme in SQL Server Reporting Services (SSRS)?
Solution
Based on your requirements I would suggest that you setup SSRS security such that you run your reports as a particular domain account then filter the data in the report based on who is actually running the report. For example, create a domain account called ReportViewer and put this account in the db_datareader role in the reporting database. Configure the report data source to run as this account, allowing the report read-only access to the data regardless of who is actually running the report. By using this approach you do not have to allow the users running the reports any access to the reporting database, since the ReportViewer account is the one actually connecting to the database.
You can get the credentials of the user that is running the report from the User!UserID global variable provided by SSRS. You'll need a table where you specify the offices that a user is allowed to access; we'll call it UserOffice. You will include an inner join to the UserOffice table in your report queries to limit the user to seeing data for the offices that they are allowed to see. You will also filter the UserOffice table to only include the rows for the user that is running the report.
Add the ReportViewer User to the db_datareader Role
Open SQL Server Management Studio (SSMS) and connect to the SQL Server instance that contains your reporting database. In my case the database is named mssqltips_reporting. Expand the SQL Server instance in the Object Explorer then expand the Security node. Right click on Logins and create a login for the ReportViewer domain account. Add the account to the db_datareader role for the reporting database as shown below:
Note that my domain is vs.local; all references to user accounts and groups in this tip will therefore begin with "vs\" followed by the user account or group.
Create the UserOffice Table
The UserOffice table specifies the list of offices that a user can access in a report. Let's assume we have an Office table that looks like this:
CREATE TABLE [dbo].[Office]( [OfficeKey] [smallint] IDENTITY(1,1) NOT NULL, [OfficeName] [nvarchar](50) NOT NULL ) |
The UserOffice table would look like this:
CREATE TABLE [dbo].[UserOffice]( [UserOfficeKey] [smallint] IDENTITY(1,1) NOT NULL, [UserAccount] [nvarchar](50) NOT NULL, [OfficeKey] [smallint] NOT NULL ) ALTER TABLE dbo.UserOffice ADD CONSTRAINT pk_UserOffice PRIMARY KEY CLUSTERED (UserAccount, OfficeKey) |
An example of a value in the UserAccount column would be vs\joe. This matches what SSRS will return as the credentials of the user running the report.
The primary key for the UserOffice table is made up of the UserAccount and OfficeKey columns so that there can't be any duplicates (which would cause our report queries to select data multiple times when joining on the OfficeKey). In addition the primary key is clustered since the typical usage of this table will be to query the OfficeKey values for a particular user.
Creating a Report
We'll create a very simple SSRS report using the Business Intelligence Development Studio (BIDS) to show how to filter the report data based on who is running the report. We will use the following query for our report:
SELECT f.Year, o.OfficeName, f.SalesAmount FROM dbo.Sales f JOIN dbo.UserOffice u ON u.OfficeKey = f.OfficeKey JOIN dbo.Office o ON o.OfficeKey = u.OfficeKey WHERE u.UserAccount = @REPORTUSER ORDER BY f.Year, o.OfficeName |
The parameter @REPORTUSER is defined in the Report Parameters as having a default value which is specified as the expression: =User!UserID SSRS sets the global variable User!UserID to the credentials of the user that is running the report. This allows us to filter our query based on who is running the report.
We will use a Matrix layout for our report as follows:
The text box at the bottom of the report layout will show the credentials of the user running the report. This will allow us to verify that the report is returning the appropriate data.
We'll deploy the report to the Report Manager using the Deploy option in BIDS.
SSRS Setup
After creating and deploying the report, there are a few things to do in the SSRS setup. We need to allow users to access the Report Manager and configure the report data source to connect to the reporting database as the ReportViewer account.
Log in as an administrator and launch the Report Manager. Report Manager is the web application that is included with SSRS. Assuming a default configuration, open your browser and navigate to http://servername/reports, where servername is the server where SSRS is deployed. Click on Site Settings; you should see the Site Settings hyperlink in the top right of your browser as shown below:
Click on "Configure site-wide security"; you will see this in the Security section as shown below:
Click the New Role Assignment button on the toolbar then fill in the dialog as shown below (remember to use your domain name instead of vs):
In this step we are allowing everyone from a particular Active Directory group to be in the System User role. The Domain Users group is a built-in one that includes all users. You may choose a group that is more restrictive than this one. You can also add users individually if necessary.
In addition to being a member of a System role, users also need to be assigned to a role which determines the tasks they can perform such as running reports, publishing reports, etc. Return to the Report Manager home and click the Properties tab, then click the New Role Assignment button. Fill in the dialog as shown below:
For our purposes we are making all users a member of the Browser role which allows them to run reports. You could choose a more restrictive group or add users individually if necessary.
Our last SSRS setup step is to edit the data source used by our report so that it always uses the ReportViewer account to connect to the reporting database. To do this navigate to your report Data Source in the Report Manager and set the credentials as shown below:
In effect what we are saying is no matter who is running the report, SSRS will connect to the reporting database as the vs\reportviewer account (remember my domain is vs). At the beginning of this tip we added the vs\reportviewer account to the db_datareader role in our reporting database, giving this user read-only access to our data.
Running the Report
In order to test the report, login as a particular user or launch Internet Explorer by right clicking on it, selecting Run As, and entering the credentials to use. I have setup two accounts for my test: Tom has access to all offices and Joe is restricted to just seeing two offices. The results of running the report as Tom are:
The results of running the report as Joe are:
As you can see, we are able to filter the data based on who is running the report.
Next Steps
| Share: | Share | Tweet |
|
![]() |
![]() |
Free SQL Server Learning |
| Thursday, July 30, 2009 - 12:11:11 PM - edward | Read The Tip |
|
I had a questoin regarding the article. Instead of configuring the data filter based on a user can I do the same thing based on a user's group or role? Thus, I would have a OfficeGroup table. Is this possible in reporting services? Thank you for your time. Edward |
|
| Thursday, July 30, 2009 - 6:26:38 PM - raybarley | Read The Tip |
|
Basing the security on groups rather than individuals is a better approach; the tip was based on an actual project where they already had a security mechanism by user and not role. As the tip points out you can determine who the user is in SSRS but there isn't anything built-in that I'm aware of As you suggest you could have an OfficeGroup table but you'll have to write some .Net code to find out if the user belongs to a particular group. I did another tip where I showed an SSIS package that extracted some data from Active Directory to populate role-based security tables in a relational database. That link is here: http://www.mssqltips.com/tip.asp?tip=1657 In the next steps of the tip there is an additional download where the sample code was changed around some to be a little easier. Take a look at the forum entry on Mon, Mar 16 2009 5:57 AM if you want the details about the revised code.
|
|
| Monday, August 17, 2009 - 12:10:38 PM - carlyk | Read The Tip |
|
How do I access the download for this? It prompts me for a user name and password. I tried my MSSQLTips login but that did not work. |
|
| Tuesday, August 18, 2009 - 6:46:24 AM - admin | Read The Tip |
|
This has been fixed in the tip, but you can download it here: http://www.mssqltips.com/tipimages2/1743_ssrs_security_sample.zip |
|
| Wednesday, August 19, 2009 - 12:08:44 PM - max420 | Read The Tip |
|
Hi First of all thank you very much for this post... It helped me big time... Now, I just had a quick follow up question on this.... Everything works exactly as you mentioned in there... I just wanted to know if there is anyway to add an exception on that query in the report.. As in WHERE u.UserAccount = @REPORTUSER except if the @REPORTUSER is "xxx".... If @REPORTUSER is "xxx" then show everything.... Please help!! Would really appreciate it... Thanks MAX |
|
| Thursday, August 20, 2009 - 6:27:21 AM - raybarley | Read The Tip |
|
Two ideas come to mind. First, you can specify an expression for your dataset query string. From the Dataset tab, click on the button with the 3 dots right next to the dataset name drop down, then click on the fx button next to the query string text box. You would use the expression something like this: =IIF(Parameters!REPORTUSER.Value = "vs\Mary", However, when I do this it doesn't work; I get an message when I try to run the report. I think this is possible but I don't know the answer. My second idea is to create a table valued function that takes the @REPORTUSER as a parameter and returns a resultset that includes all of the rows that the user is allowed to see. In this example you would return the list of OfficeKey and OfficeName then replace the join to the Office table with a join to your table valued function. Inside of your table valued function you can do whatever logic you need, such as returning all rows for a particular user, or returning selected rows based on joining to the UserOffice table for other users. I think this is better than an expression which was my first thought.
|
|
| Monday, January 09, 2012 - 12:53:01 PM - Ray Barley | Read The Tip |
|
I'm going to answer the question - How do I set the visible property (i.e. Hidden = True or False) of an object in a report from the result of a query Create a dataset that returns a single row; it can call a stored proc or just execute a SQL query. Let's call this ShowData. Create a hidden parameter that gets its list of available values from the ShowData dataset. Essentially you want the dataset to return a value that will be assigned to the hidfden parameter. Use the hidden parameter in an expression that sets the value (True or False) of the Hidden property for an object.
|
|
| Monday, January 09, 2012 - 2:20:21 PM - Jerry | Read The Tip |
|
This is very useful, and thank you very much. I wonder if you could help a bit further. Similar to other scenarios listed, I have a report with data that is broken into different areas. One area is machine specific data, the other is Human Resources specific data. Members of one AD Group might have access to all the data, while members of another AD Group only one set of the data. I have put together a SP that will return members of the AD group if provided the group name. My problem is trying to run this in an expression that is tied to Visibility. IF OBJECT_ID('tempdb..#tempsams') IS NOT NULL BEGIN DROP TABLE #tempsams END Could you help please? |
|
| Monday, January 09, 2012 - 2:57:29 PM - Ray Barley | Read The Tip |
|
An object's visibility is controlled by the Hidden property. You set it to true or false. You can set the value of the Hidden property based on an expression; e.g. =IIF(Parameters!ShowData.Value=1,True,False) In this case I have a report parameter named ShowData. You can set the value of the ShowData parameter by specifying that the available values come from a query then specify a dataset that returns a single row with a single column. You need a query (or a stored proc) that returns 1 if you want to show the data and 0 (or any other value) if you don't want to show the value. Set the visibility of the ShowData parameter to Hidden; you don't want to prompt the user for it. |
|
| Monday, January 30, 2012 - 4:17:31 PM - Rob | Read The Tip |
|
Note this doesn't work at all for report subscriptions.
|
|
| Wednesday, February 01, 2012 - 10:12:29 AM - Ray Barley | Read The Tip |
|
You are correct. Per this page: http://msdn.microsoft.com/en-us/library/ms156307.aspx The report must use stored credentials or no credentials to retrieve data at run time. You cannot subscribe to a report that is configured to use the impersonated or delegated credentials of the current user to connect to an external data source. The stored credentials can be a Windows account or a database user account. |
|
|
privacy | disclaimer | copyright | advertise | about authors | contribute | feedback | giveaways | user groups Some names and products listed are the registered trademarks of their respective owners. Edgewood Solutions LLC | MSSharePointTips.com | MSSQLTips.com |