SSRS Report Viewer + ASP.NET Credentials 401 Exception
Asked Answered
S

2

7

I have a report saved on a SQL2005 reporting server, and I want to return a rendered PDF of this report. I've figured this out when working with a local *.rdlc file (and I've blogged about it), but not when the *.rdl resides on a reporting server. I am getting a 401 Not Authorized error at the line...

reportViewer.ServerReport.SetParameters(reportDefinition.ReportParameters);

Here's the method used to render the report.

public byte[] Render(IReportDefinition reportDefinition)
{
    var reportViewer = new ReportViewer();
    byte[] renderedReport;
    try
    {
        var credentials = new WindowsImpersonationCredentials();
        reportViewer.ServerReport.ReportServerUrl = new Uri("http://myssrsbox", UrlKind.Absolute);
        reportViewer.ServerReport.ReportServerCredentials = credentials;
        reportViewer.ServerReport.ReportPath = reportDefinition.Path;
        // Exception is thrown on the following line...
        reportViewer.ServerReport.SetParameters(reportDefinition.ReportParameters);

        string mimeType;
        string encoding;
        string filenameExtension;
        string[] streams;
        Warning[] warnings;

        renderedReport = reportViewer.ServerReport.Render(reportDefinition.OutputType, reportDefinition.DeviceInfo, out mimeType, out encoding, out filenameExtension, out streams, out warnings);
    }
    catch (Exception ex)
    {
        // log the error...
        throw;
    }
    finally
    {
        reportViewer.Dispose();
    }
    return renderedReport;
}

The other thing that you're missing is the WindowsImpersonationCredentials class.

public class WindowsImpersonationCredentials : IReportServerCredentials
{
    public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
    {
        authCookie = null;
        userName = password = authority = null;
        return false;
    }

    public WindowsIdentity ImpersonationUser
    {
        get { return WindowsIdentity.GetCurrent(); }
    }

    public ICredentials NetworkCredentials
    {
        get { return null; }
    }

    public override string ToString()
    {
        return String.Format("WindowsIdentity: {0} ({1})", this.ImpersonationUser.Name, this.ImpersonationUser.User.Value);
    }
}

Other things you may need to know...

  • This is running on an intranet, and impersonation is turned on.
  • Logging indicates that the impersonation user is being set correctly.
  • This does work when running in Visual Studio (http://localhost:devport), and it does work when running on my development box (http://localhost/myApplication). It does not work when running on our test or production servers.
  • I have tried solutions both with and without system.net.defaultProxy settings in web.config. Neither worked.

What am I doing wrong? Is it a server setting? Is it code? Is it web.config?

Selfopinionated answered 17/9, 2009 at 14:27 Comment(3)
Does the impersonation user have access to the report server -- that report in particular?Fez
Have you tried running IIS under the impersonation user on your dev machine (localhost) to closer emulate what is happening on your test server? It sounds like a flat out permissions issue with your impersonation user against the reporting server or reporting server database. I assume the impersonation user is a domain account.Jowers
@NYSystemsAnalyst - Yes, the impersonation user has access to the appropriate directory on the report server.Selfopinionated
S
6

We did finally figure out the problem. Our network administrators have disabled double-hopping, so while the impersonation was correctly connecting as domain\jmeyer, the application was still attempting to connect to the SRS box with domain\web01$. Why is it set up like this? Because double-hopping is a massive security hole. (Or so I was told. Does this sound like something you would read on The Daily WTF?)

Our solution was to create a generic domain\ssrs_report_services user, and connect with that user with the following network credentials

public class CustomCredentials : IReportServerCredentials
{
    public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
    {
        authCookie = null;
        userName = password = authority = null;
        return false;
    }

    public WindowsIdentity ImpersonationUser
    {
        get { return null; }
    }

    public ICredentials NetworkCredentials
    {
        get { return new NetworkCredential("ssrs_report_services", "password", "domain") ; }
    }    
}

The above is the classic example solution that you can find all over the internets.

Selfopinionated answered 17/9, 2009 at 15:49 Comment(4)
In fact, I don't think double hopping is "allowed" under any configuration. It's not clear to me why your dev environment was working, but it was probably configured differently.Jowers
As it was explained to me, it works on dev because it doesn't count as a hop to go from localhost (as client) -> localhost (as IIS) -> SRSSelfopinionated
FYI, the admins can configure delegation on a per server basis. Check out: serverfault.com/questions/16364/…Larhondalari
By creating a new domain user, doesn't that mean that you won't be able to restrict the reports down to a user level though as everyone using you app will be authenticated using the same credentials? ThanksDiuretic
G
2

"Double hopping" is allowed - swith on Kerberos authentication ... (so long as it is working properly!)

Gaslit answered 21/9, 2009 at 14:4 Comment(1)
Unfortunately, we developers are constrained quite a bit by the environment and have no say in the infrastructure. Thanks for giving me something new to read about, though.Selfopinionated

© 2022 - 2024 — McMap. All rights reserved.