yuuvis® RAD Q&A

0 votes
by (440 points)

Hello community,
we are looking into the integration of yuuvis with another system by means of a yuuvis client action which will invoke a custom microservice in yuuvis - which in turn will integrate with the distanced system (built by us internaly).

I understand that we can secure custom microservices with the help of yuuvis gateway, so that every call to this service needs to be authenticated against an active user (we run an NTLM Gateway which means the users will not be prompted with another login).

However we are also interested in securing this custom microservice endpoint by authorization instead of authentication only - is there a way we can link some of our existing yuuvis rights or roles to the invocation of such an endpoint of a custom microservice within yuuvis?

Thanks a lot

Best,
Clemens

1 Answer

+1 vote
by (19.6k points)
selected by
 
Best answer

Hello Clemens,

you can make a rest-ws call within your microservice against the dms-service rest-ws endpoints and find out about the functional and object-type rights the user has.
For example
UserService.getState with "privileges=true&roles=true" or
DmsService.getItem with "rights=CURRENT" bzw. "rights=USER-"
You can find the username and JWT token in the header information of each rest-call.

With that information you can decide whether you want to continue or return forbidden.

Hope that helps.
Best regards
Nicolai

by (440 points)
Hello Nicolai,
thanks a lot for the response about user authorization - i hope i may add to this topic regarding gateway and you can point in the correct direction:
By now our Microservice is ready to be hosted in our yuuvis instance (it actually already is) - but i am facing difficulties securing it via the gateway.
First of all i want to go with authentication via the gateway only and maybe add the checks on user permissions later as you pointed out a possible solution.

However, although i added it to the gateway-prod.yml file, the behaviour is not as expected:

1) I can still make direct calls to the microservice hosted within yuuvis from local postman without providing any credentials. I am not getting rejected with a 401 - unauthorized, just like an unauthorized call to the rest-ws service.

This is the address i am able to call the custom microservice at
http://<ouryuuvisinstance>:<port>/<myendpoint>

2) I can not use this microservice in BPM Scripts via the name <mymicroservice> as defined in gateway-prod.yml. This approach is explained in the documentation (in this case it is not finding the microservice within yuuvis)
https://help.optimal-systems.com/yuuvis_develop/display/onpremise/BPM+Server-Side+Scripting#BPMServer-SideScripting-HTTPBody Documentation:var response = $.http    
.get()    
.service('vacation')    
.path('/status/{year}/{month}')    
.param('year', '2017')    
.param('month', '10')    
.query('includeIllness', false)    
.query('remainingHoliday', true)    
.execute(); 


This is how i tried to add the custom microservice to our gateway-prod.yml config.

  - name: 'mymicroservice'
    url: 'http://localhost:3333'


Any ideas what topic i should check in order to reach intended behaviour of the gateway with custom microservices?


Thanks a lot

Best,Clemens
by (19.6k points)
Hello Clemens,

we are using JWT Tokens (https://jwt.io/) for handling authentication. When logging in through the gateway a JWT Token is generated and always send with the headers of each call. If the token is invalid or missing you will have to login again. So if you want to forbid direct access to your microservice you will need to check for this token.

As to the BPM Scripts, I think the problem is that you did not register your microservice at the discovery service. You should use a "eureka client" from the spring boot netflix framework to register at the discovery service and then use the name used for registering your service in the bpm script.

Hope that helps.
Best regards
Nicolai
by (440 points)
edited by
Hello Nicolai,
Thanks a lot for the hint - once i tried to use the microservice name as it is registered at the discovery service instead of the name used in the gateway-prod.yml file it started to work also from within BPM scripts.


For the authentication part:
I am trying to find out how i can add this JWT validation to our custom service - can you share more details on this - maybe there is even an example of an actual yuuvis implementation ?
For example: how can i access the secret used for JWT creation in the first place by the gateway in order to validate the JWT inside the custom microservice? Is  the gateway using the global config from servicewatcher.yml  as secret for the signature?



Best,

Clemens
by (19.6k points)
Hello Clemens,

since you don't have to create a JWT Token, you don't really have to get into it that much. When the microservice receives a call with a JWT Token in the headers, you can make a call against OrganizationService.getCurrentUser with that JWT-Token in the headers and you will either get the username and all its rights / roles or a 401 if the token is invalid.
(Check http://localhost:8080/rest-ws/#ENDPOINT:OrganizationService.getCurrentUser for the query parameters documentation.)

To save performance you can cache this information for some time or (depending on your security demands) even just accept the call as authenticated if a JWT Token is present since the GatewayService already checks for validity.

Best regards
Nicolai
by (440 points)
Hello Nicolai,
i think we are getting closer - but we are not quite there yet.

We tried to implement your suggested solution to check the response of /rest-ws/service/organization/whoami while providing the JWT which gets added to the Request to the Custom Microservice.

However we receive this error (if we call the microservice from a BPM Script)

exception while JWT validation: 401 Full authentication is required to access this resource

Do you have any ideas what might be the root cause - or are there alternative solutions to validate the JWT?

Thanks a lot

Best,
Clemens
by (19.6k points)
Hello Clemens,

can you please provide the following information:
- where exactly is the exception thrown? Does it come from your microservice (and thus from the services.log file) or is it a response from the whoami endpoint (and does it show up in the dms-service.log file) ?
- can you please share the lines from your bpm script that make the rest call?

Thanks and best regards
Nicolai
by (440 points)
Hello Nicolai,
the exception is thrown in our microservice and it gets logged into the services.log file.

This is the error we see in the log:
 2019-11-19 09:48:59.989 ERROR 40896 --- [nio-4723-exec-8] c.c.d.c.DispatcherProxyController        : exception while JWT validation: 401 Full authentication is required to access this resource

This is how we call the microservice, which makes a call to an external system (already working) and tries to use the attached JWT for a call against the whoami endpoint (the idea again is to eventually first request the whoami endpoint and only call the external system if the request is accepted)

var proxyresponse = $.http
    .post()
    .service('filesenderproxy')
    .path('/dispatcherproxy')
    .header('content-type', 'application/json')
    .body(jsonbody)
    .execute();

var responsestatus = proxyresponse.status;

The status is used for further steps in the Workflow.

Best,
Clemens
by (19.6k points)
Hello Clemens,

it seems to us that the call that is made from the bpm script to your microservice already throws this exception within your microservice before it is forwarded to the rest-ws "whoami" endpoint.
The only component that we can find that outputs this error message is "spring-security-oauth2" and we do not use it. Please check if you have it (maybe by accident) in your microservice (dependency tree) and disable it.

Hope that helps
Best regards
Nicolai
by (150 points)
Hi Nicolai,

we don't really get an exception, but a 401 from the "whoami" endpoint which results in an exception within the service
So from the workflow we get an Authorization http header in our service:
JWT eyJlbmMiOiJHVUlEVkFSTEVOIiwiYWxnIjoiT1NDcnlwdCJ9...XzQ4RDUzMDJBNkI1MUY4RjA1QzNFQ0VBOEIxODUzRDQ0QjlCMzdDNDlCNzFDRjUwQjEyQjVBMTRFNEVEQkU2M0YxQjBGNzhBMDg5NEIwRkM5MjlGODBEODFBRUI0RDk4NjYxQTVGMjRGNDM5RTlGNzU2NjlEMTVGRkFGQkI0OUI1OTlERjMxNzhGRDFDMzBERDZGODE2QkFGMkRFODJEMzI.

Next we send this "JWT" as authorization http header to the "whoami" (rest-ws/service/organization/whoami) endpoint and get a HTTP 401Authentication failed! User [] with a html response:
...
<title>yuuvis RAD gateway: Es ist ein Fehler aufgetreten</title>
...
 Bei der Verarbeitung Ihrer Anfrage ist ein Fehler aufgetreten.
...
<span>401</span> (<span>Unauthorized</span>)
 

Cheers
Martin
by (19.6k points)
Hello Martin,

the problem here is that your microservice is using the gateway to communicate with the rest-ws. Instead you should be using the dms-sidecar microservice. If you are using the FeignClient you just need to annotate the class with @FeignClient("dms"). If you are using something else make sure you are using the dms-sidecar instead of the gateway.

Best regards
Nicolai
by (150 points)
Hi Nicolai,

thanks for your fast response again! :)

I did a test with the dms-sidecar. I called the sidecar and with and without using the JWT as Authorization Header.
There is no difference and I always get the following answer:

Disclaimer: I am not logged in as root! ;-)

{
    "id": "1EF9EB6E7F2F41D69D5408609CFF5FFD",
    "uri": "http://localhost:8080/rest-ws/service/organization/id/1EF9EB6E7F2F41D69D5408609CFF5FFD",
    "name": "root",
    "type": "user",
    "title": ", root",
    "email": "root@optimal-systems.de",
    "useruri": "",
    "firstname": "root",
    "lastname": "",
    "active": true,
    "hasimage": false,
    "present": true,
    "locale": "de"
}

Regarding FeignClient. Haven't used it but will give it a shot. Do you happen to have a code example?

Cheers
Martin
by (19.6k points)
Hello Martin,

if you call your custom microservice from within a bpm script it will always be called with the root user because the script is executed in the dms-service context and has no user. The microservices will then put their user in the header and this is root. If you want to get the information about the user who started the bpm step, you have to get the username / userid within the bpm script and pass it as a parameter to your microservice. you can then call "OrganizationService.getOrganizationObjectByName" to find out about the users rights and privileges.

If you call your microservice from a client custom-action, there is already a user session and the dms-sidecar will use the JWT Token from the "authorization" header. In this case you can use the whoami endpoint.

FeignClient: https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html

Best regards
Nicolai
by (150 points)
edited by
Hello Nicolai,

ok, got it. We will determine the user in the BPM script.

But what is the recommended way to secure the endpoint against requests outside the system? For example, if someone with Postman or any other tool would call the endpoint of the microservice?

Our idea was to validate the JWT in the authorization header. But how?

As described above, when we call the whoami endpoint we get the "root response" as described above. No matter if we send a valid JWT, an invalid JWT or no authorization header at all.

Kind regards
Martin

EDIT:
Could this link be helpful?
https://qa.enaio.org/index.php?qa=399&qa_1=gateway-endpoint-microservice-without-asking-credentials
I wouldn't be surprised if we have a totally crazy configuration with our gateway etc. ;-)
by (19.6k points)
Hello Martin,

the JWT validation is our recommended way. If you pass a JWT Token via the header it will not be changed to the "root response". Are you sure you have tried a valid JWT Token from a user other than root?

The link describes how to enable unauthorized access to certain endpoints. I thinks that's the opposite of what you want ;-).

Best regards
Nicolai

Related questions

...