Can I encrypt my connection string for Telerik.Reporting

2 Answers 118 Views
.NET Core Report Designer - Web Report Designer (standalone)
Bob
Top achievements
Rank 1
Iron
Iron
Iron
Bob asked on 15 Jul 2024, 09:34 PM | edited on 22 Jul 2024, 03:09 PM

My titled should be "Can I DECRYPT my connection string" - however, I cannot modify the title above.

In my .Net 8 appsettings.json config, I have these two connection strings:

"ConnectionStrings": {
  "MyReports.Properties.Settings.dev": "Data Source=Eng-Server;Initial Catalog=devsvr;Persist Security Info=True;User ID=myID;Password=myPass",
  "Encrypted.Properties.Settings.devEncr": "jjwerjwerjwerjwerjwerjwerjwejwerjwer=="
},

 

The first conn string works fine when Telerik Reporting resolves the DataSource connections at runtime, however if I try to use the Encrypted string - it does NOT work.

Can you point me to some documentation on how I can get it to work with encrypted connection strings ?

I know that the Report Config object is exposed at the ReportsController level, for example:

public class ReportsController : ReportsControllerBase
{

    public ReportsController(IReportServiceConfiguration reportServiceConfiguration, IConfiguration configuration)
        : base(reportServiceConfiguration)
    {
    }

}

 

however where would I decrypt my string ?

By the way, I also have a ClassLibrary with some custom code. I use that DLL in the standalone report designer to hook up a DataSourceObject for some custom methods I need to call. Would I possibly hook into the connect string, and decrypt it here ?

Thanks,

Bob

2 Answers, 1 is accepted

Sort by
0
Accepted
Bob
Top achievements
Rank 1
Iron
Iron
Iron
answered on 22 Jul 2024, 03:07 PM | edited on 22 Jul 2024, 03:10 PM

Hello,

We put our heads together the other day and figured out how to inject our connection string into your Resolved() method (as mentioned at the bottom of this article - https://docs.telerik.com/reporting/knowledge-base/edit-report-datasources-at-runtime). Next step is to just apply our decryption method.

For anyone else confronting this issue, here's what we did :

in Program.cs, add this code below. Pass in IConfiguration to CustomReportSourceResolver().

// Configure dependencies for ReportsController.
builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp =>
    new ReportServiceConfiguration
    {
        // The default ReportingEngineConfiguration will be initialized from appsettings.json or appsettings.{EnvironmentName}.json:
        ReportingEngineConfiguration = sp.GetService<IConfiguration>(),

        // In case the ReportingEngineConfiguration needs to be loaded from a specific configuration file, use the approach below:
        HostAppId = "HarmonyReports",
        Storage = new FileStorage(),
        ReportSourceResolver = new CustomReportSourceResolver(sp.GetService<IConfiguration>())
    });

 

Add a CustomReportResolver.cs class to your API Core project:


using Microsoft.Extensions.Hosting.Internal;
using OpenCvSharp;
using Telerik.Reporting;
using Telerik.Reporting.Services;

namespace WAPICore
{
    class CustomReportSourceResolver : IReportSourceResolver
    {
        private IConfiguration _configuration;
        private readonly IWebHostEnvironment _webHostEnvironment;
        public CustomReportSourceResolver(IConfiguration configuration) {
            _configuration = configuration;
        }
        public Telerik.Reporting.ReportSource Resolve(string reportId, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
        {
            string config = _configuration.GetSection("ConnectionStrings:SynReports.Properties.Settings.dev").Value;

            var reportPacker = new ReportPackager();
            Report report = null;
            
            using (var sourceStream = System.IO.File.OpenRead(reportId))
            {
                report = (Report)reportPacker.UnpackageDocument(sourceStream);
            }
            var sqlDS = report.GetDataSources().OfType<SqlDataSource>();
            foreach (var sqlDataSource in sqlDS)
            {
                sqlDataSource.ConnectionString = config; // DECRYPT STRING HERE 
            }

            return new InstanceReportSource() { ReportDocument = report };

        }
    }
}

 

0
Dimitar
Telerik team
answered on 18 Jul 2024, 11:32 AM

Hello Bob,

Thank you for the provided information about the scenario!

The SqlDataSource component uses the connection string as it is defined in the configuration file, thus encrypted connection strings cannot be used.

You mention that you use a custom assembly for the data, you can use the ObjectDataSource Component to decrypt the connection string and pull the data. You may find an example of how to do this in the Binding ObjectDataSource to a BusinessObject - Telerik Reporting article(without the decryption part).

Note that you will need to either pass the encrypted connection string through a data source component parameter - Using Parameters with the ObjectDataSource Component explained - Telerik Reporting, or read it from the JSON configuration file in the custom assembly - Configuration in ASP.NET Core | Microsoft Learn

An alternative to this is to keep using the SqlDataSource component, and provide to it a decrypted connection string at runtime. For the case where a web report viewer is used, this will need to happen in a custom IReportSourceResolver. The following articles will help you with implementing this:

I hope that the suggested approaches will help. Please let me know if you have any additional questions.

Regards,
Dimitar
Progress Telerik

Stay tuned by visiting our roadmap and feedback portal pages, enjoy a smooth take-off with our Getting Started resources, or visit the free self-paced technical training at https://learn.telerik.com/.
Bob
Top achievements
Rank 1
Iron
Iron
Iron
commented on 18 Jul 2024, 05:06 PM

Hi Dimitar,

Very useful articles, thank you. The "edit-report-datasources-at-runtime" seems useful, but I'm still having trouble figuring out how to actually inject the connection string into my custom assembly.

Two points:

1.. The report's ObjectDataSource sees my assembly info, so i can easily debug into my c# code when running the reports.

2. I know the API's IConfiguration and ReportServiceConfiguration are both available in my ReportsController.cs, but that's way upstream at the REST API layer. Is this where you recommend me doing it ?

Based on that code above, I can see the report info via the ReportPackager object. However I only see the connection string "name", and NOT the actual connection string. Eventually I would like to store my encrypted conn string in the telerik.dll.config, and access it downstream in order to decrypt it.

In the meantime, I'll be researching and trying some ideas...

Bob

 

Dimitar
Telerik team
commented on 22 Jul 2024, 02:11 PM

Hi Bob,

To pass the encrypted connection string to the ObjectDataSource component, you may use data source component parameters - Using Parameters with the ObjectDataSource Component explained - Telerik Reporting.

However, the Web Report Designer/Viewer does not have access to the parameters of the data source components so you should set so that the value of the data source parameter is taken from the value of a corresponding Report Parameter. Then, the encrypted connection will be passed down like this:

Report Viewer/Designer -> Report Parameter -> Data Source Component Parameter

I recommend visiting the following articles for details on how to create and use report parameters:

If this approach does not work for your needs, you may indeed need to dependency inject the configuration as shown in the Dependency injection in the ReportsController of a .NET Core application - Telerik Reporting KB article.

Regarding how the connections are saved in the SqlDataSource component, indeed, by default the connection will be saved by its name, not the actual connection string. At runtime, the engine will use that name and look it up in the loaded IConfiguration instance to resolve the connection string from there. We call this a "shared" connection but it is also possible to embed the actual connection string in the report definition during the SqlDataSource wizardConnecting to a SQL Database at a Glance - Telerik Reporting.

This way, if you go with the route of implementing a custom resolver, the encrypted connection strings will be present and you will be able to assign decrypted connections to your data source components following the Modify the Report Data Sources through code - Telerik Reporting article.

 

 

Bob
Top achievements
Rank 1
Iron
Iron
Iron
commented on 22 Jul 2024, 03:13 PM | edited

Hi,

As per https://docs.telerik.com/reporting/api/telerik.reporting.services.ireportsourceresolver, we were able to successfully inject our IConfiguration into your reports. Next step is to apply the decrypt routine.

NB: The c# Resolve() method below will be hit for EVERY report that is launched from the front end. In our case the Telerik Viewer template in our TypeScript code here:

    constructor(private readonly appSession: AppSession, private readonly config: AppConfigService) {
        this.serviceUrl = this.config.host + GG.SV_REPORTS;
        this.telerikTemplateUrl = "assets/templates/telerikReportViewerTemplate-18.1.24.514.html";

        this.reportSource = {
            report: "Reports\\ReportCatalog.trdp",
            parameters: {
                UserID: ...,
                SessionID: ...,
            },
        };
    }

 

And our new class in the .Net 8 API project

using Microsoft.Extensions.Hosting.Internal; using OpenCvSharp; using Telerik.Reporting; using Telerik.Reporting.Services; namespaceWAPICore { classCustomReportSourceResolver : IReportSourceResolver { private IConfiguration _configuration; privatereadonly IWebHostEnvironment _webHostEnvironment; public CustomReportSourceResolver(IConfiguration configuration) { _configuration = configuration; } public Telerik.Reporting.ReportSource Resolve(string reportId, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues) { string config = _configuration.GetSection("ConnectionStrings:SynReports.Properties.Settings.dev").Value; var reportPacker = new ReportPackager(); Report report = null; using (var sourceStream = System.IO.File.OpenRead(reportId)) { report = (Report)reportPacker.UnpackageDocument(sourceStream); } var sqlDS = report.GetDataSources().OfType<SqlDataSource>(); foreach (var sqlDataSource in sqlDS) { sqlDataSource.ConnectionString = config; // DECRYPT CONN STR HERE } returnnew InstanceReportSource() { ReportDocument = report }; } } }

 

 

and in Program.cs of your .Net 8 API project (pass IConfiguration into your new class):

// Configure dependencies for ReportsController.
builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp =>
    new ReportServiceConfiguration
    {
        // The default ReportingEngineConfiguration will be initialized from appsettings.json or appsettings.{EnvironmentName}.json:
        ReportingEngineConfiguration = sp.GetService<IConfiguration>(),

        // In case the ReportingEngineConfiguration needs to be loaded from a specific configuration file, use the approach below:
        HostAppId = "SynReports",
        Storage = new FileStorage(),
        ReportSourceResolver = new CustomReportSourceResolver(sp.GetService<IConfiguration>())
    });


Bob
Top achievements
Rank 1
Iron
Iron
Iron
commented on 22 Jul 2024, 04:27 PM

A couple minor things we're trying to figure out, though.

1. The"navigate-to" property in the Report Designer.

We created a c# list of our ReportInfo class and added the path to the report. This way we can avoid hard-coding it in the report itself. So when the report's ObjectDataSource calls into our custom assembly, it returns the list below...

return reportInfoList =
     [

         new ReportInfo("Orders", @".\Reports\Orders.trdp", "A collection of order-related reports", true);
         new ...
   ]

        

But that only works well from the ReportsCatalog, at the top level. Once you drill into a detailed report, can I specify the path in the Resolve() method ? Perhaps when I unpack the report, or via the reportID param coming into the Resolve() - can I resolve the path and "trdp" extension here ?


        public Telerik.Reporting.ReportSource Resolve(string reportId, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
        {
            string config = _configuration.GetSection("ConnectionStrings:SynergyReports.Properties.Settings.syndev").Value;

            var reportPacker = new ReportPackager();
            Report report = null;
            
            using (var sourceStream = System.IO.File.OpenRead(reportId))
            {
                report = (Report)reportPacker.UnpackageDocument(sourceStream);
            }
            var sqlDS = report.GetDataSources().OfType<SqlDataSource>();
            foreach (var sqlDataSource in sqlDS)
            {
                sqlDataSource.ConnectionString = config;

                Rijndael.Decrypt("test", "test", "Test", "test", 111, "test", 222);           // DECRYPT !!
            }

            return new InstanceReportSource() { ReportDocument = report };

        }

 

 

Dimitar
Telerik team
commented on 23 Jul 2024, 02:51 PM

Hi Bob,

For the Navigate To Report action, when it is triggered, a new report source object will be loaded in the report viewer, and it will be resolved through the IReportDocumentResolver. If you need to perform operations on the navigated report, the correct solution is to implement this interface.

The second example from the Using Custom ReportSource Resolver and Custom ReportDocument Resolver - Telerik Reporting KB article covers the scenario of implementing a custom document resolver, please have a look at it and let me know if you have any further questions.

Bob
Top achievements
Rank 1
Iron
Iron
Iron
commented on 24 Jul 2024, 04:55 PM | edited

I believe your first article re: Navigate-to-report should do the trick, esp. since we already have our custom resolver working great now.

And once again, thank you so much for your assistance.

Bob
Top achievements
Rank 1
Iron
Iron
Iron
commented on 24 Jul 2024, 05:01 PM | edited

One more thing re: the OLD Telerik Report .net framework platform.

At one point I was able to open the old report designer .cs files in my old .Net Framework Solution.

However, it's very glitchy at times. And suddenly I can no longer open it anymore in my old framework project (VS2022).

I do have the .msi install package on my local, and I definitely did have the old version up in design mode - even in VS2022.

I'm wondering if the telerik.reporting versioning is the issue.


And yes, I do have your extensions enabled.

 

And, our NEW .Net 8 project is working fine. And the standalone designer also working. However, I need to only reference the old version to see what they were doing exactly in some old reports.

 

thanks

Dimitar
Telerik team
commented on 26 Jul 2024, 12:55 PM

Hi Bob,

The Visual Studio Report Designer works only with the last installed Reporting version. If you have multiple Reporting versions installed on the machine, you will be able to edit CS/VB Reports from the last installed version.

You may run the Upgrade Wizard to upgrade/downgrade the projects with the reports to the last installed version.

Tags
.NET Core Report Designer - Web Report Designer (standalone)
Asked by
Bob
Top achievements
Rank 1
Iron
Iron
Iron
Answers by
Bob
Top achievements
Rank 1
Iron
Iron
Iron
Dimitar
Telerik team
Share this question
or