Saturday, January 31, 2009

Sql Reporting Services 2005 - The request failed with HTTP status 401: Unauthorized.

When calling a render method for a report ala:

result = ws.Render(report, format, historyid, devinfo, parameters, credentials, showhide, out encoding, out mimetype, out reporthistoryparams,
out warnings, out streamid);

you receive:

The request failed with HTTP status 401: Unauthorized.



[WebException: The request failed with HTTP status 401: Unauthorized.]
System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall) +551137
System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters) +204
TestReportingServices.localhost.ReportingService.Render(String Report, String Format, String HistoryID, String DeviceInfo, ParameterValue[] Parameters, DataSourceCredentials[] Credentials, String ShowHideToggle, String& Encoding, String& MimeType, ParameterValue[]& ParametersUsed, Warning[]& Warnings, String[]& StreamIds) in c:\Projects\BidTrackerReporting\TestReportingServices\Web References\localhost\Reference.cs:1426
TestReportingServices._Default.btnGenerateReport_Click(Object sender, EventArgs e) in c:\Projects\BidTrackerReporting\TestReportingServices\Default.aspx.cs:80
System.Web.UI.WebControls.Button.OnClick(EventArgs e) +105
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +107
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +7
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +11
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1746




Line 1424: [return: System.Xml.Serialization.XmlElementAttribute("Result", DataType="base64Binary")]
Line 1425: public byte[] Render(string Report, string Format, string HistoryID, string DeviceInfo, ParameterValue[] Parameters, DataSourceCredentials[] Credentials, string ShowHideToggle, out string Encoding, out string MimeType, out ParameterValue[] ParametersUsed, out Warning[] Warnings, out string[] StreamIds) {
Line 1426: object[] results = this.Invoke("Render", new object[] {
Line 1427: Report,
Line 1428: Format,



The wrong (or none) credentials are specified to connect to the PHYSICAL files on your system (not the database). If using a console application (or windows form) this may work ok for you, but switching to asp.net it may fail. Your current credentials will be passed over in the interactive console or windows form application so you may not have a problem there, but in asp.net will be running under the identity of either the app pool, network service, or IUSR_MachineName and thus won't have enough permissions. Thankfully, the solution is easy though. I've seen all sorts of articles for using custom permissions, but it's straight forward, just add new network credentials to the proxy class that is generated for the webservice
/reportserver/reportservice.asmx

just add this to an instance of your proxy class (of course filling in your details:
ws.Credentials = new System.Net.NetworkCredential("your login", "your password", "your domain");

Now, there is another way around this. In IIS (I'll use IIS 7 here since that is what is on my box) you can just enable permissions for a particular user on the files. In one case, you can make it generic, and add permissions for the IUSR account for read access to the reportserver folder.
Set these permissions on C:\Program Files\Microsoft SQL Server\MSSQL.4\Reporting Services\ReportServer (or whatever folder it is on your system)


Then in IIS, enable anonymous access for the folder.
I tried setting "physical path credentials" in IIS 7 as well, then the application wouldn't have to pass them in, however then you still have the ability for users to view /reportserver, but it didn't work as would be expected - ie access still denied.

so as for allowing all IUSR - I DONT RECOMMEND THIS.

Why?

1. if you do not disable the reportserver for outside access then ANYONE can view at least some details about your reports by simply navigating to https://yourserver/reportserver (although they may not be able to run your reports if you require prompting for credentials or dont store any credendtials and expect them to be passed in as part of the app)
2. Why not just include the account in the code your application uses to access the reports?
3. Who wants the risk when dealing with reports?


but if you really want to try this and enable it only for local access (this may be just fine for your environment if you are running this system on a separate network that a webserver in a dmz accesses and the report server is not accessible to the outside world. Although if the web server is compromised then an attacker could access the report server. As of now in Vista's IIS 7 UI, you cannot set the ip restriction - they left the UI element for it out. If you want to set this in Vista, you need to install IIS 7 Admin Pack. You MUST FIRST INSTALL SP1 for Vista otherwise you can cause the admin console to fail. There's a nice blog about the pack at:
http://blogs.msdn.com/carlosag/archive/2008/03/24/IISAdminPackRequestFiltering.aspx
The module in question is IpRestrictionModule
for the admin pack see:
http://www.iis.net/downloads
http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1682


If you want to limit to a particular address in IIS see:
--------------------------------
http://technet.microsoft.com/en-us/library/cc730889.aspx
--------------------------------
To configure an IPv4 address restriction for the management service
Open IIS Manager. For information about opening IIS Manager, see IIS 7.0: Open IIS Manager.

In the Connections pane, click the server node in the tree.

In Features View, double-click Management Service.

On the Management Service page, in the Actions pane, click Stop to stop the management service.

Select the Enable remote connections box.

Under IPv4 Address Restrictions, use one of the following procedures:

Click Allow to allow an IPv4 address or range of IPv4 addresses to connect to the management service.

Click Deny to deny an IPv4 address or range of IPv4 addresses from connecting to the management service.

In the Add Allow Connection Rule dialog box or Add Deny Connection Rule dialog box, use one of the following procedures, and then click OK:

To allow or deny a specific IP address, click Specific IPv4 address, and then type the IPv4 address in the box.

To allow or deny a range of IP addresses, click IPv4 address range, type an IPv4 address in the box, and then type a subnet mask in the Subnet mask box.

In the Actions pane, click Apply and then click Start.

---------------------------------------------


Now for the better solution, try the following code.
This was just from some local testing, but should work:

byte[] result;
string report = "/SomeCustomReportFolder/SomeReport";
localhost.DataSourceCredentials[] credentials = null;
string showHide = null;
string encoding="";
string mimeType="";
string format = "PDF";
string historyId = null;
string devinfo = "";
localhost.Warning[] warnings = null;
localhost.ParameterValue[] reportHistoryParams = null;
string[] streamId = null;
localhost.SessionHeader sh = new localhost.SessionHeader();

localhost.ReportingService ws = new TestReportingServices.localhost.ReportingService();
ws.SessionHeaderValue = sh;
ws.Credentials = new System.Net.NetworkCredential("your login", "your password", "your domain");

localhost.ParameterValue[] parameters = { new TestReportingServices.localhost.ParameterValue() };
parameters[0].Name = "parameter name"; //Case SENSITIVE!!!
parameters[0].Value = "parameter value";
result = ws.Render(report, format, historyid, devinfo, parameters, credentials, showhide, out encoding, out mimetype, out reporthistoryparams,
out warnings, out streamid);



enjoy : )

1 comment: