Skip to content

Commit

Permalink
Fb/new uaa commands (#63)
Browse files Browse the repository at this point in the history
* new uaa commands 1

* re-recorded screencast

* basic wiring for userinfo

* more wiring for userinfo

* more wiring for userinfo 2

* oauth with username password

* add username password to oauth

* remove redundant initializer

* more spacing

* slightly better naming

* oh happy testing

* oh happy testing 2

* oh happy testing 3

* oh happy testing 4

* fix missing service credentials identity zone

* oh happy testing 5

* oh happy testing 6

* oh happy testing 7

* oh happy testing 8

* polish

* docs
  • Loading branch information
rlindner81 authored Feb 16, 2024
1 parent 29ae1d5 commit e70a12f
Show file tree
Hide file tree
Showing 11 changed files with 1,135 additions and 386 deletions.
19 changes: 12 additions & 7 deletions docs/assets/record-cli.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# dependencies

brew install gifsicle
brew install imagemagick

# tools
https://github.com/asciinema/asciinema
https://github.com/asciinema/asciicast2gif
Expand All @@ -10,10 +15,10 @@ sed -i '' 's/file:\/\/C02FLAC2MD6M\/Users\/d057156/file:\/\//g' dev/mtx-tool/doc
sed -i '' 's/d057156@C02FLAC2MD6M ~ %/%/g' dev/mtx-tool/docs/assets/*.cast

# convert to gif
asciicast2gif dev/mtx-tool/docs/assets/tool-setup-setup.cast dev/mtx-tool/docs/tool-setup/tool-setup-setup.gif
asciicast2gif dev/mtx-tool/docs/assets/user-authentication-service.cast dev/mtx-tool/docs/user-authentication/user-authentication-service.gif
asciicast2gif dev/mtx-tool/docs/assets/tenant-registry-list.cast dev/mtx-tool/docs/tenant-registry/tenant-registry-list.gif
asciicast2gif dev/mtx-tool/docs/assets/cap-multitenancy-list.cast dev/mtx-tool/docs/cap-multitenancy/cap-multitenancy-list.gif
asciicast2gif dev/mtx-tool/docs/assets/cap-multitenancy-upgrade-tenant.cast dev/mtx-tool/docs/cap-multitenancy/cap-multitenancy-upgrade-tenant.gif
asciicast2gif dev/mtx-tool/docs/assets/hana-management-list.cast dev/mtx-tool/docs/hana-management/hana-management-list.gif
asciicast2gif dev/mtx-tool/docs/assets/hana-management-tunnel.cast dev/mtx-tool/docs/hana-management/hana-management-tunnel.gif
npx asciicast2gif dev/mtx-tool/docs/assets/tool-setup-setup.cast dev/mtx-tool/docs/tool-setup/tool-setup-setup.gif
npx asciicast2gif dev/mtx-tool/docs/assets/user-authentication-service.cast dev/mtx-tool/docs/user-authentication/user-authentication-service.gif
npx asciicast2gif dev/mtx-tool/docs/assets/tenant-registry-list.cast dev/mtx-tool/docs/tenant-registry/tenant-registry-list.gif
npx asciicast2gif dev/mtx-tool/docs/assets/cap-multitenancy-list.cast dev/mtx-tool/docs/cap-multitenancy/cap-multitenancy-list.gif
npx asciicast2gif dev/mtx-tool/docs/assets/cap-multitenancy-upgrade-tenant.cast dev/mtx-tool/docs/cap-multitenancy/cap-multitenancy-upgrade-tenant.gif
npx asciicast2gif dev/mtx-tool/docs/assets/hana-management-list.cast dev/mtx-tool/docs/hana-management/hana-management-list.gif
npx asciicast2gif dev/mtx-tool/docs/assets/hana-management-tunnel.cast dev/mtx-tool/docs/hana-management/hana-management-tunnel.gif
11 changes: 5 additions & 6 deletions docs/assets/user-authentication-service.cast
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
[0.058819, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
[0.06013, "o", "\u001b]7;file://\u0007"]
[0.063811, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J% \u001b[K\u001b[?2004h"]
[0.655193, "o", "m"]
[0.772656, "o", "\bmt"]
[0.655193, "o", "mt"]
[1.109352, "o", "x"]
[1.260759, "o", " "]
[1.58929, "o", "u"]
[1.741168, "o", "a"]
[1.893037, "o", "a"]
[2.125524, "o", "s"]
[2.125524, "o", "sc"]
[2.301198, "o", " "]
[2.660844, "o", "d"]
[2.82937, "o", "e"]
Expand Down Expand Up @@ -38,15 +37,15 @@
[7.446992, "o", "n"]
[7.582325, "o", "y"]
[7.943242, "o", "\u001b[?2004l\r\r\n"]
[8.031216, "o", "running --uaa-service destination skyfin-company\r\n"]
[8.031216, "o", "running --uaa-service-client destination skyfin-company\r\n"]
[8.032386, "o", "targeting cf api https://api.cf.sap.hana.ondemand.com / org \"skyfin\" / space \"dev\"\r\n"]
[8.610559, "o", "using global cache for \"afc-backend\"\r\n"]
[8.819952, "o", "Authorization:\r\nBearer eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vc2t5ZmluLWNvbXBhbnkuYXV0aGVudGljYXRpb24uc2FwLmhhbmEub25kZW1hbmQuY29tL3Rva2VuX2tleXMiLCJraWQiOiJkZWZhdWx0LWp3dC1rZXktLTEyNDY1MTM5OTAiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiJkNzhlMWQzNTYyNzM0N2Y4OWNlMGU0YWI2OWI2MzU0YSIsImV4dF9hdHRyIjp7ImVuaGFuY2VyIjoiWFNVQUEiLCJzdWJhY2NvdW50aWQiOiI1ZWNjNzQxMy0yYjdlLTQxNGEtOTQ5Ni1hZDRhNjFmNmNjY2YiLCJ6ZG4iOiJza3lmaW4tY29tcGFueSIsInNlcnZpY2VpbnN0YW5jZWlkIjoiMDVlZDAyNjAtNWYwMS00OTlhLWIwNzctMmEzY2FmYzVlYWMzIn0sInN1YiI6InNiLWNsb25lMDVlZDAyNjA1ZjAxNDk5YWIwNzcyYTNjYWZjNWVhYzMhYjU4NzR8ZGVzdGluYXRpb24teHNhcHBuYW1lIWI0MzMiLCJhdXRob3JpdGllcyI6WyJ1YWEucmVzb3VyY2UiXSwic2NvcGUiOlsidWFhLnJlc291cmNlIl0sImNsaWVudF9pZCI6InNiLWNsb25lMDVlZDAyNjA1ZjAxNDk5YWIwNzcyYTNjYWZjNWVhYzMhYjU4NzR8ZGVzdGluYXRpb24teHNhcHBuYW1lIWI0MzMiLCJjaWQiOiJzYi1jbG9uZTA1ZWQwMjYwNWYwMTQ5OWFiMDc3MmEzY2FmYzVlYWMzIWI1ODc0fGRlc3RpbmF0aW9uLXhzYXBwbmFtZSFiNDMzIiwiYXpwIjoic2ItY2xvbmUwNWVkMDI2MDVmMDE0OTlhYjA3NzJhM2NhZmM1ZWFjMyFiNTg3NHxkZXN0aW5hdGlvbi14c2FwcG5hbWUhYjQzMyIsImdyYW50X3"]
[8.820052, "o", "R5cGUiOiJjbGllbnRfY3JlZGVudGlhbHMiLCJyZXZfc2lnIjoiZTVlYThlZjQiLCJpYXQiOjE2NTY2Nzc1MzUsImV4cCI6MTY1NjcyMDczNSwiaXNzIjoiaHR0cHM6Ly9za3lmaW4tY29tcGFueS5hdXRoZW50aWNhdGlvbi5zYXAuaGFuYS5vbmRlbWFuZC5jb20vb2F1dGgvdG9rZW4iLCJ6aWQiOiI1ZWNjNzQxMy0yYjdlLTQxNGEtOTQ5Ni1hZDRhNjFmNmNjY2YiLCJhdWQiOlsic2ItY2xvbmUwNWVkMDI2MDVmMDE0OTlhYjA3NzJhM2NhZmM1ZWFjMyFiNTg3NHxkZXN0aW5hdGlvbi14c2FwcG5hbWUhYjQzMyIsInVhYSJdfQ.pjjktHnuK9oc5fUF8tytpAqpzmfO9SjfIT3x2HyNEXie1Qu2le7kdg50sBo8yMb4AgdbZpBsl762G2-f961qXyY2NiBS-LDNFCh56xewPz6XxiEAt9s6K2xhQhGHhO7h0Armtu9Bp7akThnTwWbESZwEVAqseEO5D9iFNTOnCQKeyZBXWgYkkppOz4Lyak9dM3hyKCS1I1zSOUO2ualTBEjoEQKJM1WovFeZTbsYc6yGe2PAR2weaATs1TLQ-dVRoemOORZ5emIMQZD9BFtuXO0m0deAKUWCwiVUYX3VS68K_wpS_PdP_cy7AEp8BASwHKVoKK4AlVitwZRTe-YlxA\r\n"]
[8.825308, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
[8.825713, "o", "\u001b]7;file://\u0007"]
[8.825824, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J% \u001b[K\u001b[?2004h"]
[9.72801, "o", "mtx uaas destination skyfin-company"]
[9.72801, "o", "mtx uaasc destination skyfin-company"]
[10.135465, "o", " "]
[10.447474, "o", "-"]
[10.591563, "o", "-"]
Expand All @@ -57,7 +56,7 @@
[12.143707, "o", "d"]
[12.687541, "o", "e"]
[12.991754, "o", "\u001b[?2004l\r\r\n"]
[13.07854, "o", "running --uaa-service destination skyfin-company --decode\r\n"]
[13.07854, "o", "running --uaa-service-client destination skyfin-company --decode\r\n"]
[13.079689, "o", "targeting cf api https://api.cf.sap.hana.ondemand.com / org \"skyfin\" / space \"dev\"\r\n"]
[13.693805, "o", "using global cache for \"afc-backend\"\r\n"]
[13.917561, "o", "JWT Header:\r\n{\"alg\":\"RS256\",\"jku\":\"https://skyfin-company.authentication.sap.hana.ondemand.com/token_keys\",\"kid\":\"default-jwt-key--1246513990\",\"typ\":\"JWT\"}\r\n\r\nJWT Body:\r\n{\r\n \"jti\": \"6d5b0d10b74f4e96a3df264d6b16bb66\",\r\n \"ext_attr\": {\r\n \"enhancer\": \"XSUAA\",\r\n \"subaccountid\": \"5ecc7413-2b7e-414a-9496-ad4a61f6cccf\",\r\n \"zdn\": \"skyfin-company\",\r\n \"serviceinstanceid\": \"05ed0260-5f01-499a-b077-2a3cafc5eac3\"\r\n },\r\n \"sub\": \"sb-clone05ed02605f01499ab0772a3cafc5eac3!b5874|destination-xsappname!b433\",\r\n \"authorities\": [\r\n \"uaa.resource\"\r\n ],\r\n \"scope\": [\r\n \"uaa.resource\"\r\n ],\r\n \"client_id\": \"sb-clone05ed02605f01499ab0772a3cafc5eac3!b5874|destination-xsappname!b433\",\r\n \"cid\": \"sb-clone05ed02605f01499ab0772a3cafc5eac3!b5874|destination-xsappname!b433\",\r\n \"azp\": \"sb-clone05ed02605f01499ab0772a3cafc5eac3!b5874|destination-xsappname!b433\",\r\n \"grant_type\": \"client_credentials\",\r\n \"rev_sig\": \"e5ea8ef4\",\r\n \"iat\": 1656677540,\r\n \"exp\": 1656720740,\r\n \"iss\": \"https://skyfin-company.authentication.sap"]
Expand Down
17 changes: 10 additions & 7 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,16 @@ commands:
setcc --clean-cache clean all app caches
=== user authentication (uaa) ===
~ uaad --uaa-decode TOKEN decode JSON web token
~ uaac --uaa-client [TENANT] obtain token for generic client
~ uaap --uaa-passcode PASSCODE [TENANT] obtain token for uaa one-time passcode
~ uaai --uaa-userinfo PASSCODE [TENANT] detailed user info for passcode
~ uaas --uaa-service SERVICE [TENANT] obtain token for uaa trusted service
... [TENANT] obtain token for tenant, fallback to paas tenant
... --decode decode result token
~ uaad --uaa-decode TOKEN decode JSON web token
~ uaac --uaa-client [TENANT] obtain uaa token for generic client
~ uaap --uaa-passcode PASSCODE [TENANT] obtain uaa token for one-time passcode
~ uaau --uaa-user USERNAME PASSWORD [TENANT] obtain uaa token for username password
~ uaasc --uaa-service-client SERVICE [TENANT] obtain service token for generic client
~ uaasp --uaa-service-passcode SERVICE PASSCODE [TENANT] obtain service token for one-time passcode
~ uaasu --uaa-service-user SERVICE USERNAME PASSWORD [TENANT] obtain service token for username password
... [TENANT] obtain token for tenant, fallback to paas tenant
... --decode decode result token
... --userinfo add detailed user info for passcode or username
=== tenant registry (reg) ===
~ regl --registry-list [TENANT] list all subscribed subaccount names
Expand Down
59 changes: 38 additions & 21 deletions docs/user-authentication/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ Commands for this area are:

```
=== user authentication (uaa) ===
~ uaad --uaa-decode TOKEN decode JSON web token
~ uaac --uaa-client [TENANT] obtain token for generic client
~ uaap --uaa-passcode PASSCODE [TENANT] obtain token for uaa one-time passcode
~ uaai --uaa-userinfo PASSCODE [TENANT] detailed user info for passcode
~ uaas --uaa-service SERVICE [TENANT] obtain token for uaa trusted service
... [TENANT] obtain token for tenant, fallback to paas tenant
... --decode decode result token
~ uaad --uaa-decode TOKEN decode JSON web token
~ uaac --uaa-client [TENANT] obtain uaa token for generic client
~ uaap --uaa-passcode PASSCODE [TENANT] obtain uaa token for one-time passcode
~ uaau --uaa-user USERNAME PASSWORD [TENANT] obtain uaa token for username password
~ uaasc --uaa-service-client SERVICE [TENANT] obtain service token for generic client
~ uaasp --uaa-service-passcode SERVICE PASSCODE [TENANT] obtain service token for one-time passcode
~ uaasu --uaa-service-user SERVICE USERNAME PASSWORD [TENANT] obtain service token for username password
... [TENANT] obtain token for tenant, fallback to paas tenant
... --decode decode result token
... --userinfo add detailed user info for passcode or username
~ are read-only commands
```
Expand All @@ -58,23 +61,23 @@ privileges that particular user has. In order to achieve this:
passcode. In our example it is `https://skyfin-company.authentication.sap.hana.ondemand.com/passcode`.
- Using the user's one-time passcode run `mtx uaap vjcOkwoVFL4ig17YIebYJYgKODSK6rsL skyfin-company --decode`.

## Accessing Server APIs
## Accessing APIs as Technical User

If your server exposes an endpoint with JWT authentication, which is the default in CAP, then you can
access these endpoint with a generic client JWT that mtx can get for you.

| purpose | command |
| :------------------------------------------------- | :---------------------------------- |
| obtain client JWT for provider subaccount (paas) | `mtx uaac` |
| obtain client JWT for subscriber subaccount (saas) | `mtx uaac <subdomain or tenant id>` |
| purpose | command |
| :------------------------------------------------ | :---------------------------------- |
| obtain client JWT of provider subaccount (paas) | `mtx uaac` |
| obtain client JWT of subscriber subaccount (saas) | `mtx uaac <subdomain or tenant id>` |

Set the `Authorization` header as `Bearer <jwt>` in HTTP requests for the endpoints to pass validation. We use
[Postman](https://www.postman.com) for this, but any other HTTP client works as well.
[curl](https://curl.se) for this, but any other HTTP client works as well.

## Access And Userinfo With Passcode
## Accessing APIs as User

Instead of acting as a generic client for the server, you can access server APIs as a regular user, provided the user
in question gives you a one-time-passcode, that their tenant's UAA published.
Instead of acting as a generic client, you can access APIs as a regular user, provided the user in question gives you
a one-time-passcode, that their tenant's UAA published.

As an example, let's say the user in question works for a tenant with subdomain `microogle` in the BTP region `eu10`,
then they can obtain their passcode by logging in at
Expand All @@ -83,10 +86,10 @@ then they can obtain their passcode by logging in at
Using this one-time passcode, you can either get a regular JWT for accesses _as that user_, or their extended user
info:

| purpose | command |
| :------------------------------- | :--------------------------------------------- |
| obtain user JWT | `mtx uaap <passcode> <subdomain or tenant id>` |
| obtain extended user information | `mtx uaai <passcode> <subdomain or tenant id>` |
| purpose | command |
| :-------------------------- | :-------------------------------------------------------- |
| obtain user JWT | `mtx uaap <passcode> <subdomain or tenant id>` |
| obtain user JWT + user info | `mtx uaap <passcode> <subdomain or tenant id> --userinfo` |

Like before, if the command is run without the `subdomain or tenant_id` parameter, the tool will assume you mean the
provider subaccount (paas).
Expand All @@ -104,11 +107,25 @@ If these assertions match `user_attributes` that UAA expects, see
`xs-security.json` allows `"foreign-scope-references": ["user_attributes"]`, then they will show up as extended user
information.

Similarly to using a one-time passcode, you can access APIs as some user if you have both the username and password of
that user.

| purpose | command |
| :-------------------------- | :------------------------------------------------------------------- |
| obtain user JWT | `mtx uaau <username> <password> <subdomain or tenant id>` |
| obtain user JWT + user info | `mtx uaau <username> <password> <subdomain or tenant id> --userinfo` |

## Accessing Service APIs

You can access services in the same way that the server accesses them, usually for debugging purposes. You will
need to know the label of the service and it needs to be bound to the app which is configured for user authentication.

| purpose | command |
| :---------------------------- | :------------------------------------------------------------------- |
| obtain client JWT for service | `mtx uaasc <service> <subdomain or tenant id>` |
| obtain user JWT for service | `mtx uaasp <service> <passcode> <subdomain or tenant id>` |
| obtain user JWT for service | `mtx uaasu <service> <username> <password> <subdomain or tenant id>` |

For example `destination` is the label of the [BTP destination service](https://help.sap.com/docs/CP_CONNECTIVITY).
You will also need to choose a trusted subdomain to use, for example `skyfin-company`.

Expand All @@ -117,7 +134,7 @@ If the subdomain belongs to the
xsapp provider subaccount, then it will always be trusted. If the subdomain belongs to a different subaccount, it will
only be trusted if that account has successfully subscribed to the xsapp.

So, `mtx uaas destination skyfin-company`, will give you the corresponding JWT, which can then be decoded or used as
So, `mtx uaasc destination skyfin-company`, will give you the corresponding JWT, which can then be decoded or used as
`Authorization` header in an HTTP client.

## Example for Saas Service
Expand Down
Binary file modified docs/user-authentication/user-authentication-service.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 10 additions & 7 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ commands:
setcc --clean-cache clean all app caches
=== user authentication (uaa) ===
~ uaad --uaa-decode TOKEN decode JSON web token
~ uaac --uaa-client [TENANT] obtain token for generic client
~ uaap --uaa-passcode PASSCODE [TENANT] obtain token for uaa one-time passcode
~ uaai --uaa-userinfo PASSCODE [TENANT] detailed user info for passcode
~ uaas --uaa-service SERVICE [TENANT] obtain token for uaa trusted service
... [TENANT] obtain token for tenant, fallback to paas tenant
... --decode decode result token
~ uaad --uaa-decode TOKEN decode JSON web token
~ uaac --uaa-client [TENANT] obtain uaa token for generic client
~ uaap --uaa-passcode PASSCODE [TENANT] obtain uaa token for one-time passcode
~ uaau --uaa-user USERNAME PASSWORD [TENANT] obtain uaa token for username password
~ uaasc --uaa-service-client SERVICE [TENANT] obtain service token for generic client
~ uaasp --uaa-service-passcode SERVICE PASSCODE [TENANT] obtain service token for one-time passcode
~ uaasu --uaa-service-user SERVICE USERNAME PASSWORD [TENANT] obtain service token for username password
... [TENANT] obtain token for tenant, fallback to paas tenant
... --decode decode result token
... --userinfo add detailed user info for passcode or username
=== tenant registry (reg) ===
~ regl --registry-list [TENANT] list all subscribed subaccount names
Expand Down
36 changes: 28 additions & 8 deletions src/cliOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const PASS_ARG = Object.freeze({
TENANT: "TENANT", // either subdomain or tenant_id
SUBDOMAIN: "SUBDOMAIN",
PASSCODE: "PASSCODE",
USERNAME: "USERNAME",
PASSWORD: "PASSWORD",
SERVICE: "SERVICE",
JOB_ID: "JOB_ID",
TENANT_ID: "TENANT_ID",
Expand All @@ -26,6 +28,7 @@ const FLAG_ARG = Object.freeze({
DECODE: "--decode",
REVEAL: "--reveal",
TIMESTAMPS: "--time",
USER_INFO: "--userinfo",
AUTO_UNDEPLOY: "--auto-undeploy",
SKIP_UNCHANGED: "--skip-unchanged",
});
Expand Down Expand Up @@ -59,23 +62,40 @@ module.exports = {
commandVariants: ["uaap", "--uaa-passcode"],
requiredPassArgs: [PASS_ARG.PASSCODE],
optionalPassArgs: [PASS_ARG.TENANT],
optionalFlagArgs: [FLAG_ARG.DECODE],
optionalFlagArgs: [FLAG_ARG.DECODE, FLAG_ARG.USER_INFO],
callback: uaa.uaaPasscode,
readonly: true,
},
UAA_USERINFO: {
commandVariants: ["uaai", "--uaa-userinfo"],
requiredPassArgs: [PASS_ARG.PASSCODE],
UAA_USER: {
commandVariants: ["uaau", "--uaa-user"],
requiredPassArgs: [PASS_ARG.USERNAME, PASS_ARG.PASSWORD],
optionalPassArgs: [PASS_ARG.TENANT],
callback: uaa.uaaUserInfo,
optionalFlagArgs: [FLAG_ARG.DECODE, FLAG_ARG.USER_INFO],
callback: uaa.uaaUser,
readonly: true,
},
UAA_SERVICE: {
commandVariants: ["uaas", "--uaa-service"],
UAA_SERVICE_CLIENT: {
commandVariants: ["uaasc", "--uaa-service-client"],
requiredPassArgs: [PASS_ARG.SERVICE],
optionalPassArgs: [PASS_ARG.TENANT],
optionalFlagArgs: [FLAG_ARG.DECODE],
callback: uaa.uaaService,
callback: uaa.uaaServiceClient,
readonly: true,
},
UAA_SERVICE_PASSCODE: {
commandVariants: ["uaasp", "--uaa-service-passcode"],
requiredPassArgs: [PASS_ARG.SERVICE, PASS_ARG.PASSCODE],
optionalPassArgs: [PASS_ARG.TENANT],
optionalFlagArgs: [FLAG_ARG.DECODE, FLAG_ARG.USER_INFO],
callback: uaa.uaaServicePasscode,
readonly: true,
},
UAA_SERVICE_USER: {
commandVariants: ["uaasu", "--uaa-service-user"],
requiredPassArgs: [PASS_ARG.SERVICE, PASS_ARG.USERNAME, PASS_ARG.PASSWORD],
optionalPassArgs: [PASS_ARG.TENANT],
optionalFlagArgs: [FLAG_ARG.DECODE, FLAG_ARG.USER_INFO],
callback: uaa.uaaServiceUser,
readonly: true,
},

Expand Down
Loading

0 comments on commit e70a12f

Please sign in to comment.