connect icon indicating copy to clipboard operation
connect copied to clipboard

[BUG] MirthConnect Version 4.5.0 - Java 17 - Web Service Sender - button "Get Operations" fails if authentication is needed

Open javandre opened this issue 10 months ago • 13 comments

Describe the bug In a "Web Service Sender" destination the button "Get Operations" produces an error when "Authentication" is set to "Yes" and the WSDL server requires authentication. Bug is consistently reproducible.

To Reproduce

  1. Create new channel
  2. Set destination to "Web Service Sender"
  3. Enable Authentication, fill in username and password
  4. Enter URL to WSDL server (ensure that this WSDL server needs an authentication)
  5. Click "Get Operations"

Expected behavior Get operations from WSDL Server without error.

Actual behavior Java-Error occurs (screenshot 1 and 2), operations are not loaded.

Screenshots Screenshot 1: Java Error Window image

Screenshot 2: Error in mirth.log file image

Environment:

  • OS: Linux (Ubuntu 22.04), Windows 11
  • Java: OpenJDK 17
  • Connect Version 4.5.0
    • the Java9+ entries in the vmoptions file are included

Workaround Fill in the needed WSDL operations manually.

Additional context Hint: The destination works correctly as a WebServiceSender if you fill in the operations manually. Only clicking the button "Get Operations" fails.

javandre avatar Apr 17 '24 12:04 javandre

Hi @javandre Thanks for posting this Bug.

HappyHampi avatar Apr 17 '24 13:04 HappyHampi

image

and

the Java9+ entries in the vmoptions file are included

I think you need additional Java9 entries for the WebService sender to work.

This is still a bug in MC, since the suggested options should support the software that NextGen publishes. I think a workaround would be to add this to your VM options file: --add-opens=java.base/java.util.concurrent=ALL-UNNAMED

jonbartels avatar Apr 17 '24 14:04 jonbartels

Hi @jonbartels We tried this today without success.

image

HappyHampi avatar Apr 17 '24 14:04 HappyHampi

@hanspeterbrun read the latest error message. It is still a modules error but it is a DIFFERENT module.

You need additional opens entries

jonbartels avatar Apr 17 '24 15:04 jonbartels

Hi @jonbartels

Thanks for your comments. We added this java.io entry, too. After this we got no more errors in mirth.log, but a HTTP 401 error as response. I think that no username/password was transmitted with the request, because the destination could not read the input textfields.

javandre avatar Apr 17 '24 19:04 javandre

Are you using hardcoded credentials or mapped values? "Test Connection" usually does not work with mapped values.

Can you change the URL to a server you control and see what it sends? You should see the appropriate authn headers. The request won't work but you should be able to see what Mirth is transmitting.

jonbartels avatar Apr 18 '24 13:04 jonbartels

I'm a little bit confused, I think we are talking about the button "Get Operations" (not "Test Connection")...

Yes, I'm using hardcoded credentials and I'm sure there are correct (tested with SoapUI).

Unfortunately I don't have a SOAP server under my control at the moment. I'll try to check this later and then comment here.

javandre avatar Apr 18 '24 16:04 javandre

You don't need a soap server.

e.g. if you have node installed:

npx http-echo-erver // starts a dumb http listener on port 3000 and echos request to console

pacmano1 avatar Apr 18 '24 18:04 pacmano1

Actaully tried this - and it does not send authorization.

# npx http-echo-server
[server] event: listening (port: 3000)
[server] event: connection (socket#1)
[socket#1] event: resume
[socket#1] event: readable
[socket#1] event: end
[socket#1] event: prefinish
[socket#1] event: finish
[socket#1] event: close
[server] event: connection (socket#2)
[socket#2] event: resume
[socket#2] event: data
--> GET / HTTP/1.1
--> Host: localhost:3000
--> Connection: Keep-Alive
--> User-Agent: Apache-HttpClient/4.5.13 (Java/17.0.10)
--> Accept-Encoding: gzip,deflate
--> 
--> 
[socket#2] event: prefinish
[socket#2] event: finish
[socket#2] event: readable
[socket#2] event: end
[socket#2] event: close

pacmano1 avatar Apr 18 '24 18:04 pacmano1

Workaround of course is just the grab the WSDL and host it anywhere you can in fact access.

pacmano1 avatar Apr 18 '24 18:04 pacmano1

@pacmano1 seems to have reproduced the issue.

@javandre you should edit your bug report to focus ONLY on the WSDL auth issue. The opens issues were unrelated.

The relevant code that generates the auth error is: https://github.com/nextgenhealthcare/connect/blob/c1bbf981b396279984124adbd50ddb4851b2bc76/client/src/com/mirth/connect/connectors/ws/WebServiceSender.java#L1157

This generates the credentials in the URL: https://github.com/nextgenhealthcare/connect/blob/c1bbf981b396279984124adbd50ddb4851b2bc76/server/src/com/mirth/connect/connectors/ws/WebServiceConnectorServlet.java#L197

This generates the credentials in the format https://user:pass@domain/.... . This condition is not visible in @pacmano1 test case. It also seems plausible that this format is not accepted by many servers. @pacmano1 you might need to re-try your test case with nc or a different tool to see if the credentials are being transmitted correctly.

@javandre what authentication mechanism is the SOAP server expecting you to use for the WSDL? HTTP Basic? Oauth? something else?

Using a browser or curl can you retrieve the WSDL with the https://user:pass@domain/.... format?

jonbartels avatar Apr 18 '24 19:04 jonbartels

I think that I have found the problem. In this commit prior to the version 4.5.0, the existing SoapUI library that handled WSDL requests (com.eviware.soapui.impl.wsdl.support.wsdl.WsdlLoader) was replaced by the Java native javax.wsdl.xml.WSDLReader one.

The root of the problem seems to be that Mirth Connect is placing the Basic auth credentials from the connector form directly in the URL. This worked for SoapUI library, but I think that it is forbidden on purpose in the native Java implementation at some level. In fact it makes sense, since for example I have just found a URL with credentials in the console of my server instance. This could cause passwords to be leaked for some.

I can think of a few ways to solve this bug in no particular order:

  • Rolling back to the previous library. I don't believe it is an option, as I suppose that if they have migrated to the native Java package it is for a reason.
  • Setting a default java.net.Authenticator. For me this is the best and the intended way of using this library and it even supports more authentication methods. Sadly, it is not very straightforward, as we are not able to pass the credentials directly. The only way I found to do it seems to define it for the entire application. My idea is to define a single Authenticator for the Mirth Connect instance with access to a in-memory a pair of user/password for every URL that is being requested. By identifying the requested URL using getRequestingURL() method, we could return the right authentication credentials for each URL. Maybe just after returning a pair of credentials once, they might be automatically deleted.
  • Passing a WSDLLocator object. Instead of passing a String, the library supports receiving an object that implements the WSDL download part, offering an InputSource with the XML. Here seems to be an implementation from an Apache abandoned project.
  • Using another library to handle this. This would probably require an adaptation in order to process other generated objects.
  • Downloading the WSDL and processing it entirely. It has the advantage of not depending on a dedicated library. Probably the hardest of all and the most prone to errors.

@jonbartels netcat or similar will not work, since at least the Mirth Connect versions prior to 4.5.0 used the HTTP authentication framework, so they wait for the server to ask back for the password if needed (The client is expecting a 401 error code along with a WWW-Authenticate header before sending the password). A simple way to reproduce this bug could be to create another channel with an HTTP listener and with Basic authentication. Also, the bug doesn't have to do with the server receiving credentials in other format, as Basic authentication credentials are always sent and received in the same way. The user:password URL syntax part is totally interpreted by the client to know which credentials it has to use in the server without asking the user or obtaining them in any other way.

I also suggest a simple way to workaround this issue. It is to create another channel with an HTTP listener and an HTTP sender that acts as a proxy for the other channel, requesting the WSDL for it using the appropriate credentials. After using it, it could simply be disabled or removed.

jluengo-meditecs avatar Apr 29 '24 17:04 jluengo-meditecs

I just realized another simpler way to solve it without changing much!

We could just simply define a default Authenticator implementation that gets the user:password part of the URL if it exists and then returns it.

I even have just wrote a Rhino script that if executed once will even fix the issue until the server is restarted. It can be put for example in the deploy script of any channel or in the global deploy script.

The following works, but the username and password parsing has to be done better to comply with RFC 1738.

any ":", "@", or "/" must be encoded

var url_userinfo_authenticator = new java.net.Authenticator({
    getPasswordAuthentication: function() {
        var userInfo = null;
        try {
            var url = new java.net.URL(this.getRequestingURL());
            userInfo = url.getUserInfo();
        } catch (e) {
            e.printStackTrace();
        }

        if (userInfo != null) {
            var parts = userInfo.split(":");
            if (parts.length === 2) {
                var username = parts[0];
                var password = parts[1];
                return new java.net.PasswordAuthentication(username, password.toCharArray());
            }
        }

        return null;
    }
});

java.net.Authenticator.setDefault(url_userinfo_authenticator);

jluengo-meditecs avatar Apr 29 '24 17:04 jluengo-meditecs