How can I force the .NET ASMX WSDL generation to change a soap:address location from http to https? - wsdl

Using VB.NET asmx project, which is hosted behind SSL offload, I need to change the generated WSDL to show https for the soap:address.
from: <soap:address location="http://example.com/example.asmx"/>
to: <soap:address location="https://example.com/example.asmx"/>
preferably outside of code so we can influence in the build process.

It depends what system are you using for generating the wsdl.
You shared that you are using VB.NET but it does not narrow down enough to answer your question a 100%. If you can show some code then we could help hopefully. Also as far as I remember, the location in the WSDL file is the same as the client is accessing it (the URL where it reaches). Meaning that as the offloading happens elsewhere the location could always be http.
Without further information I see three options for you:
Configure the TLS offloader to redirect the queries from http to httpS. (This is also a recommended setting from a security point of view.)
Where the offloading is happening use a solution to replace the content of the response. (This has the advantage of being specific to the environment.)
Use self singed certificate on the internal application as well, and therefore the address will be generated correctly. (This could be a bit tougher nut to crack, but has the benefit of not being dependent on other configuration and having to modify that configuration for every environment from development to live.)
In c# it could be done in code https://learn.microsoft.com/en-us/archive/blogs/kaevans/modify-a-web-services-wsdl-using-a-soapextensionreflector and is qite complicated. If you have a developer machine, then you need to use TLS as well... but here you go:
using System;
using System.Web.Services.Description;
namespace Msdn.Web.Services.Samples
{
public class HttpsReflector : SoapExtensionReflector
{
public override void ReflectMethod()
{
//no-op
}
public override void ReflectDescription()
{
ServiceDescription description = ReflectionContext.ServiceDescription;
foreach (Service service in description.Services)
{
foreach (Port port in service.Ports)
{
foreach (ServiceDescriptionFormatExtension extension in port.Extensions)
{
SoapAddressBinding binding = extension as SoapAddressBinding;
if (null != binding)
{
binding.Location = binding.Location.Replace("https://", "https://");
}
}
}
}
}
}
}

Related

Async Function Fails when called as part of a Constructor

I'm rather new to Blazor, but I am currently trying to get access to some classes from within a class library that I've created and deployed as a Nuget package. As background, the Nuget package is an Api library, which allows me to talk to a webservice (I don't know if this is relevant or not). However, every time I go to the page where I'm testing, the page never loads and instead I left looking at the browser loading circle until I navigate away or close the application. During my testing here, it seems like it's the #inject call of my interface into the Blazor component which is causing the issue as when I remove it and try to load the page normally, the page does so.
So to demonstrate what I have setup, here is where I've added the Singletons to the DI:
builder.Services.AddSingleton<IApiConfigHelper, ApiConfigHelper>();
builder.Services.AddSingleton<IApiHelper, ApiHelper>();
builder.Services.AddSingleton<ISystemEndpoint, SystemEndpoint>();
Then on the blazor page, I have the following declarations at the top of my page:
#using Library.Endpoints
#using Library.Models
#page "/"
#inject ISystemEndpoint _systemEndpoint
Now I am leaning towards is this something to do with the Nuget package and using it with DI. I have tested the library away from this project (In a console application) and can confirm it's working as it should.
I have also created a local class library as a test to, to see if I could inject a data access class into the page and I can confirm that this works without an issue, which suggests to me that DI is working, just not with my Nuget package.
I did have a look into CORS, given that the Nuget package is accessing an external domain, and setup the following simple CORS policy in the app:
builder.Services.AddCors(policy =>
{
policy.AddPolicy("OpenCorsPolicy", opt =>
opt.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
Which is added to the app after the AddRouting call like so:
app.UseCors("OpenCorsPolicy");
However again, this wasn't the solution so if anyone is able to point me in the right direction with where I may be going wrong with this or offer any advice, I would be most grateful.
EDIT 1 - Provides details #mason queried
Regarding SystemEndpoint, the constructor is being injected with 2 things, as below:
public SystemEndpoint(IApiHelper apiHelper, IOptions<UriConfigModel> uriOptions)
{
_apiHelper = apiHelper;
_uriOptions = uriOptions.Value;
}
My Nuget Library is dependant on the following:
Azure.Identity
Azure.Security.KeyVault.Secrets
Microsoft.AspNet.WebApi.Client
Microsoft.Extensisons.Options.ConfigurationExtensions
EDIT 2 - Doing some further testing with this I have added a simple Endpoint class to my Nuget library, which returns a string with a basic message, as well as returning the values of the 2 UriConfig properties as below. I added this test to 1) sanity check that my DI was working correctly, and 2) check the values that are being assigned from appsettings to my UriConfig Object.
public class TestEndpoint : ITestEndpoint
{
private readonly IOptions<UriConfigModel> _uriConfig;
public TestEndpoint(IOptions<UriConfigModel> uriConfig)
{
_uriConfig = uriConfig;
}
public string TestMethod()
{
return $"You have successfully called the test method\n\n{_uriConfig.Value.Release} / {_uriConfig.Value.Version}";
}
}
However when adding in the dependency of IApiHelper into the Ctor, the method then breaks and fails to load the page. Looking into ApiHeloer, the Ctor has a dependency being injected into it of IApiConfigHelper. Looking at the implementation, the Ctor of ApiConfigHelper is setting up the values and parameters of the HttpClient that should make the REST calls to the external Api.
Now I believe what is breaking the code at this point is a call I'm making to Azure Key Vault, via REST, to pull out the secret values to connect to the Api. The call to KeyVault is being orchestrated via the following method, making use of the Azure.Security.KeyVault.Secrets Nuget Package, however I assume that at the heart of it, it's making a REST call to Azure on my behalf:
private async Task<KeyVaultSecret> GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = await _secretClient.GetSecretAsync(secretName);
return result.Value;
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
So that's where I stand with this at the moment. I still believe it could be down to CORS, as it seems to be falling over when making a call to an external service / domain, but I still can say 100%. As a closing thought, could it be something as simple as when I call call the above method, it's not being awaited????
So after persisting with this it seems like the reason it was failing was down to "awaiting" the call to Azure KeyVault, which was happening indirectly via the constructor of ApiConfigHelper. The resulting method for getting KeyVault value is now:
private KeyVaultSecret GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = _secretClient.GetSecret(secretName);
if (result is not null)
{
return result.Value;
}
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
I am now able to successfully make calls to my library and return values from the Api it interacts with.
I can also confirm that this IS NOT a CORS issue. Once I saw that removing the await was working, I then removed the CORS policy declarations from the service and the app in my Blazor's start-up code and everything continued to work without an issue.
As a final note, I must stress that this is only seems an issue when using the library with Blazor (possibly webApi projects) as I am able to use the library, awaiting the Azure call just fine in a console application.

How can I know if my app is running under Kestrel or HTTP.sys?

What's the best way to know if my app is running under Kestrel or HTTP.sys. All that I have found so far is to check for "Kestrel" in one of the HttpConext property class names.
Within an MVC controller I can do something like this:
Boolean IsKestrel = HttpContext.Features.GetType().ToString().Contains("Kestrel");
i.e. check this:
Features = {Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection<Microsoft.AspNetCore.Hosting.HostingApplication.Context>}
When using HTTP.sys I only get:
Features = {Microsoft.AspNetCore.Http.Features.FeatureCollection}
(Is "Http" here enough to know that this is HTTP.sys?)
There must be a better way. Is there an obvious property somewhere that contains the name of the host being used?
A broader question might be, how do I know what the builder pattern built?
Update
Found something better, but still looking for a Property that has the server name or type.
In an MVC controller:
var isKestrel = HttpContext.Request.Headers.GetType().ToString().Contains(".Kestrel.");
var isHTTPsys = HttpContext.Request.Headers.GetType().ToString().Contains(".HttpSys.");
At the operating system level, netsh http show servicestate will list all active URLs listening via HTTP.SYS.
From code you can locate an instance of Microsoft.AspNetCore.Hosting.Server.IServer and check what its implementation is, in netcore 6:
Kestrel => Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl
IIS ==> Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer
HTTP.SYS => Microsoft.AspNetCore.Server.HttpSys.MessagePump
This relies on implementation details (so can break), also other extensions can change these e.g. CoreWcf creates CoreWCF.Configuration.WrappingIServer that wraps one of the above implementations.
you can use System.Diagnostics.Process.GetCurrentProcess().ProcessName
I am not sure whether you want to check this information using the code only or you are just looking for a way to know on which web server your app is running.
In my search result, I found that we could set the ports for a specific web server. When the application will run on that specific web server then it will use that pre-configured port. I am assuming your app also has a similar configuration. You could set the different ports for Kestrel, Http.sys, or IIS. By checking the port number you could say that on which web server your site is running.
You could try to go to the launchSettings.json file in your project where you could configure ports for IIS and Kestral.
Helpful References:
Kestrel Web Server in ASP.NET Core
Understand HTTP.sys Web Server In ASP.NET Core
Hello this is a good question, you question is asking how to find out from inside the code and not from a console.
OOB I did not find anything. So, I had to get very creative to figure this out, sorry for the typo's its brand new stuff...
Option 1:
Since the Kestrel section & endpoints are inside the appsettings.json I used that to find out if its hosted by Kestrel!
//Please create a static class to hold the config.
public static class MyStartupIsItKestrelConfiguration
{
public static IConfiguration Configuration;
public bool static IsKestrel()
{
//check your section kestrel??
var kestrel = configuration.GetSection("Kestrel");
// now check kestrel section or any other section
// see picture for kestrel endpoint in app setting sbelow
return true;
}
}
Now you can access it anywhere and see if you used Kestrel
//Now add it/save it in your startup and access later
public Startup(IConfiguration configuration)
{
Configuration = configuration;
MyStartupIsItKestrelConfiguration.Configuration = configuration;
}
Once you have this
//you can use it in ** YOUR CONTROLLER
MyStartupIsItKestrelConfiguration.IsKestrel();
Option 2:
Please check this public Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { get; }
You can get the features public TFeature? Get<TFeature> (); as a Key Value Pair - and then check the feature for e.g. KESTREL DOES NOT ALLOW PORT SHARING
they split the features namespace in .net core 6 there are breaking changes
You should use the features collection

The anti-forgery token could not be decrypted

I have a form:
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()...
and action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl, string City)
{
}
occasionally (once a week), I get the error:
The anti-forgery token could not be decrypted. If this application is
hosted by a Web Farm or cluster, ensure that all machines are running
the same version of ASP.NET Web Pages and that the configuration
specifies explicit encryption and validation keys. AutoGenerate cannot
be used in a cluster.
i try add to webconfig:
<machineKey validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps" />
but the error still appears occasionally
I noticed this error occurs, for example when a person came from one computer and then trying another computer
Or sometimes an auto value set with incorrect data type like bool to integer to the form field by any jQuery code please also check it.
I just received this error as well and, in my case, it was caused by the anti-forgery token being applied twice in the same form. The second instance was coming from a partial view so wasn't immediately obvious.
validationKey="AutoGenerate"
This tells ASP.NET to generate a new encryption key for use in encrypting things like authentication tickets and antiforgery tokens every time the application starts up. If you received a request that used a different key (prior to a restart for instance) to encrypt items of the request (e.g. authenication cookies) that this exception can occur.
If you move away from "AutoGenerate" and specify it (the encryption key) specifically, requests that depend on that key to be decrypted correctly and validation will work from app restart to restart. For example:
<machineKey
validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
validation="SHA1"
decryption="AES"
/>
You can read to your heart's content at MSDN page: How To: Configure MachineKey in ASP.NET
Just generate <machineKey .../> tag from a link for your framework version and insert into <system.web><system.web/> in Web.config if it does not exist.
Hope this helps.
If you get here from google for your own developer machine showing this error, try to clear cookies in the browser. Clear Browser cookies worked for me.
in asp.net Core you should set Data Protection system.I test in Asp.Net Core 2.1 or higher.
there are multi way to do this and you can find more information at Configure Data Protection and Replace the ASP.NET machineKey in ASP.NET Core and key storage providers.
first way: Local file (easy implementation)
startup.cs content:
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
Configuration = configuration;
WebHostEnvironment = webHostEnvironment;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment WebHostEnvironment { get; }
// This method gets called by the runtime.
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// .... Add your services like :
// services.AddControllersWithViews();
// services.AddRazorPages();
// ----- finally Add this DataProtection -----
var keysFolder = Path.Combine(WebHostEnvironment.ContentRootPath, "temp-keys");
services.AddDataProtection()
.SetApplicationName("Your_Project_Name")
.PersistKeysToFileSystem(new DirectoryInfo(keysFolder))
.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
}
}
second way: save to db
The Microsoft.AspNetCore.DataProtection.EntityFrameworkCore NuGet
package must be added to the project file
Add MyKeysConnection ConnectionString to your projects
ConnectionStrings in appsettings.json > ConnectionStrings >
MyKeysConnection.
Add MyKeysContext class to your project.
MyKeysContext.cs content:
public class MyKeysContext : DbContext, IDataProtectionKeyContext
{
// A recommended constructor overload when using EF Core
// with dependency injection.
public MyKeysContext(DbContextOptions<MyKeysContext> options)
: base(options) { }
// This maps to the table that stores keys.
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
startup.cs content:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime.
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// ----- Add this DataProtection -----
// Add a DbContext to store your Database Keys
services.AddDbContext<MyKeysContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyKeysConnection")));
// using Microsoft.AspNetCore.DataProtection;
services.AddDataProtection()
.PersistKeysToDbContext<MyKeysContext>();
// .... Add your services like :
// services.AddControllersWithViews();
// services.AddRazorPages();
}
}
If you use Kubernetes and have more than one pod for your app this will most likely cause the request validation to fail because the pod that generates the RequestValidationToken is not necessarily the pod that will validate the token when POSTing back to your application. The fix should be to configure your nginx-controller or whatever ingress resource you are using and tell it to load balance so that each client uses one pod for all communication.
Update: I managed to fix it by adding the following annotations to my ingress:
https://kubernetes.github.io/ingress-nginx/examples/affinity/cookie/
Name Description Values
nginx.ingress.kubernetes.io/affinity Sets the affinity type string (in NGINX only cookie is possible
nginx.ingress.kubernetes.io/session-cookie-name Name of the cookie that will be used string (default to INGRESSCOOKIE)
nginx.ingress.kubernetes.io/session-cookie-hash Type of hash that will be used in cookie value sha1/md5/index
I ran into this issue in an area of code where I had a view calling a partial view, however, instead of returning a partial view, I was returning a view.
I changed:
return View(index);
to
return PartialView(index);
in my control and that fixed my problem.
I got this error on .NET Core 2.1. I fixed it by adding the Data Protection service in Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection();
....
}
you are calling more than one the #Html.AntiForgeryToken() in your view
I get this error when the page is old ('stale'). A refresh of the token via a page reload resolves my problem. There seems to be some timeout period.
I found a very interesting workaround for this problem, at least in my case. My view was dynamically loading partial views with forms in a div using ajax, all within another form. the master form submits no problem, and one of the partials works but the other doesn't. The ONLY difference between the partial views was at the end of the one that was working was an empty script tag
<script type="text/javascript">
</script>
I removed it and sure enough I got the error. I added an empty script tag to the other partial view and dog gone it, it works! I know it's not the cleanest... but as far as speed and overhead goes...
I know I'm a little late to the party, but I wanted to add another possible solution to this issue. I ran into the same problem on an MVC application I had. The code did not change for the better part of a year and all of the sudden we started receiving these kinds of error messages from the application.
We didn't have multiple instances of the anti-forgery token being applied to the view twice.
We had the machine key set at the global level to Autogenerate because of STIG requirements.
It was exasperating until I got part of the answer here: https://stackoverflow.com/a/2207535/195350:
If your MachineKey is set to AutoGenerate, then your verification
tokens, etc won't survive an application restart - ASP.NET will
generate a new key when it starts up, and then won't be able to
decrypt the tokens correctly.
The issue was that the private memory limit of the application pool was being exceeded. This caused a recycle and, therefore, invalidated the keys for the tokens included in the form. Increasing the private memory limit for the application pool appears to have resolved the issue.
My fix for this was to get the cookie and token values like this:
AntiForgery.GetTokens(null, out var cookieToken, out var formToken);
For those getting this error on Google AppEngine or Google Cloud Run, you'll need to configure your ASP.NET Core website's Data Protection.
The documentation from the Google team is easy to follow and works.
https://cloud.google.com/appengine/docs/flexible/dotnet/application-security#aspnet_core_data_protection_provider
A general overview from the Microsoft docs can be found here:
https://cloud.google.com/appengine/docs/flexible/dotnet/application-security#aspnet_core_data_protection_provider
Note that you may also find you're having to login over and over, and other quirky stuff going on. This is all because Google Cloud doesn't do sticky sessions like Azure does and you're actually hitting different instances with each request.
Other errors logged, include:
Identity.Application was not authenticated. Failure message: Unprotect ticket failed

How do I make my WCF proxy asp.net session-dependent?

I have a (hand-coded) WCF proxy layer (i.e. not a ServiceReference) that handles all client-side functionality and does some caching for a WCF application (served under IIS 7). I can run, say, two desktop clients, and each one will have its own proxy, and their cached objects (some of which are static or singleton) will function independently, since each proxy runs in its own process, of course.
Now, I have an asp.net MVC website (not a technology I know well), and I want this same behavior: when two different clients connect to the web server, I want two different instances of the proxy. I'm assuming this has something to do with asp.net sessions, but don't know where to go next to hook up the asp.net session and the proxy instance. Is there a way to do this out of the box?
Put another way, how do I make an IIS server under asp.net use a WCF proxy as a client, and not treat it as a server?
Assuming that you can refactor your code such that your proxy is an instance, you would be able to use something like the following to access it in the ASP.NET application:
public class WcfProxy
{
private WcfProxy(){}
private static object _locker = new object();
public static WcfProxy Instance
{
get
{
if (HttpContext.Current.Session != null)
{
if (HttpContext.Current.Session["ProxyInstance"] == null)
{
lock (_locker)
{
if (HttpContext.Current.Session["ProxyInstance"] == null)
{
HttpContext.Current.Session["ProxyInstance"] = new WcfProxy();
}
}
}
return (WcfProxy) HttpContext.Current.Session["ProxyInstance"];
}
}
}
public void OpenChannel(){}
}
Note that this omits locking. Usage:
WcfProxy.Instance.OpenChannel();

Caching in JavaFX WebView

We're developing a JavaFX 2.x application, which needs to provide some GIS support. We've come to conclusion that using GoogleMaps via an embedded WebView is the quickest option. The problem with it is that every time our application starts, the corresponding JavaScript libraries are downloaded. This makes development very difficult as it takes a couple of second before anything interactive can be done on the WebView panel.
The first thing that comes to mind is to have some sort of caching like web browsers do in order to store the libraries and read them locally when needed. How can this be achieved with WebView? Are there any alternatives to caching in our case?
Thanx.
The WebView component doesn't provide caching of web resources out of the box. However, it does utilize the java.net stack for network communications. What this means is you can install your own URL Handler that talks to a cache and serves resources from that cache. For example, put something like this block in your main() method before the JavaFX launch call:
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
public URLStreamHandler createURLStreamHandler(String protocol) {
if ( "http".equals(protocol) ) {
return new URLStreamHandler() {
protected URLConnection openConnection(URL u) throws IOException {
if ( isCached(u) ) {
return new CachedStreamConnection(u);
}
return new MyURLConnection(u);
}
};
}
// Don't handle a non-http protocol, so just return null and let
// the system return the default one.
return null;
}
});
Of course the devil is in the details. You should take the cache policies returned by the HTTP headers (like ETags) into consideration when storing a resource in your cache. Another consideration is HTML meta tags. Here is a good resource on caching.
You may also want to consider a cookie management system to complement this cache management system.

Resources