provectus / kafka-ui

Open-Source Web UI for Apache Kafka Management
Apache License 2.0
9.5k stars 1.16k forks source link

OIDC/oAuth2 + Okta Groups Extraction #4226

Open daviddyball opened 1 year ago

daviddyball commented 1 year ago

Issue submitter TODO list

Describe the bug (actual behavior)

I ahve configured oAuth2/OIDC for AuthN + AuthZ and configured my Okta authorization server to include groups and can confirm that groups appear in the access token it returns.

When I use rbac rules targeting a group which should be extracted from groups in the access token I get this error on the console:

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.ClassCastException: Cannot cast
com.provectus.kafka.ui.config.auth.RbacOidcUser to org.springframework.security.core.userdetails.UserDetails

My rbac section in the config looks like this:

      rbac:
        roles:
        - clusters:
          - development-dev-1
          name: "ReadOnly"
          permissions:
          - resource: clusterconfig
            actions: [ VIEW ]
          - resource: topic
            value: ".*"
            actions: [ VIEW, MESSAGES_READ ]
          - resource: consumer
            value: ".*"
            actions: [ VIEW ]
          - resource: schema
            value: ".*"
            actions: [ VIEW ]
          - resource: connect
            value: ".*"
            actions: [ VIEW, RESTART ]
          - resource: acl
            actions: [ VIEW ]
          subjects:
          - provider: "oauth"
            type: "group"
            value: "Developers"

And my auth section looks like this:

      auth:
        oauth2:
          client:
            okta:
              authorization-grant-type: authorization_code
              client-name: Okta
              custom-params:
                roles-field: groups
                type: oauth
              issuer-uri: https://myorg.okta-emea.com/oauth2/{authorizationServerId}
              provider: okta
              redirect-uri: https://{hostname}/kafka-ui/login/oauth2/code/okta
              scope:
              - openid
              - profile
              - email
              - groups
              user-name-attribute: email
        type: OAUTH2

Setting logging.level.ROOT: TRACE I was able to pull out the decoded access token that is getting returned... it looks like this (with redactions):

[
  Authentication=OAuth2AuthenticationToken [
    Principal=RbacOidcUser[
      user=Name: [myuser@myorg.com],
      Granted Authorities: [
        [OIDC_USER, SCOPE_email, SCOPE_groups, SCOPE_openid, SCOPE_profile]
      ],
      User Attributes: [
        {
          at_hash=<HASH>,
          sub=<SUBJECT_ID>,
          zoneinfo=America/Los_Angeles,
          ver=1,
          email_verified=true,
          amr=[swk, mfa, pwd],
          iss=https://myorg.okta-emea.com/oauth2/<AUTHZ_SERVER_ID>,
          groups=[Developers],
          preferred_username=myuser@myorg.com,
          locale=en_US,
          given_name=My,
          nonce=<NONCE>,
          aud=[<AUDIENCE_ID>],
          updated_at=<DATE>,
          idp=<IDP_ID>,
          auth_time=<DATE>,
          name=My User,
          exp=<DATE>,
          family_name=User,
          iat=<DATE>,
          email=myuser@myorg.com,
          jti=<REDACTED>
        }
      ],
      groups=[]
    ],
    Credentials=[PROTECTED],
    Authenticated=true,
    Details=null,
    Granted Authorities=[OIDC_USER, SCOPE_email, SCOPE_groups, SCOPE_openid, SCOPE_profile]
  ]
]

As you can see under User Attributes groups is indeed listed with the correct group name (Developers) that should match the subject value for the ReadOnly role defined in the rbac config.

Expected behavior

I'd expect the system to find the expected groups property inside the User Attributes section of the access token and correctly assign the role with "subjects": {"provider": "oauth", "type": "group", "value": "Developers"}.

Your installation details

  1. App version: cc12814
  2. Helm chart version: 0.7.5
  3. Config
    rbac:
     roles:
     - clusters:
       - development-1
       name: "ReadOnly"
       permissions:
       - resource: clusterconfig
         actions: [ VIEW ]
       - resource: topic
         value: ".*"
         actions: [ VIEW, MESSAGES_READ ]
       - resource: consumer
         value: ".*"
         actions: [ VIEW ]
       - resource: schema
         value: ".*"
         actions: [ VIEW ]
       - resource: connect
         value: ".*"
         actions: [ VIEW, RESTART ]
       - resource: acl
         actions: [ VIEW ]
       subjects:
       - provider: "oauth"
         type: "group"
         value: "Developers"
    auth:
     oauth2:
       client:
         okta:
           authorization-grant-type: authorization_code
           client-name: Okta
           custom-params:
             roles-field: groups
             type: oauth
           issuer-uri: https://myorg.okta-emea.com/oauth2/<AUTHZ_SERVER_ID>
           provider: okta
           redirect-uri: https://kafka-ui/login/oauth2/code/okta
           scope:
           - openid
           - profile
           - email
           - groups
           user-name-attribute: email
     type: OAUTH2
    kafka:
     clusters:
     - bootstrapServers: development-1-kafka-bootstrap:9092
       kafka-connect:
       - address: http://development-1-connect-api:8083
         name: development-1
       name: development-1
       schemaRegistry: http://schema-registry:8081
    logging:
     level:
       ROOT: TRACE
  4. Any IAC configs: N/A

Steps to reproduce

  1. Deploy Kafka UI with the aforementioned configuration using an OIDC provider from Okta.
  2. Attempt to login with a valid user that is assigned to a group that should end up with the ReadOnly role.
  3. No role is assigned and no resources are listed in the UI.

Screenshots

No response

Logs

DEBUG [parallel-2] o.s.s.w.s.c.WebSessionServerSecurityContextRepository: Found SecurityContext 'SecurityContextImpl [Authentication=OAuth2AuthenticationToken [Principal=RbacOidcUser[user=Name: [myuser@myorg.com], Granted Authorities: [[OIDC_USER, SCOPE_email, SCOPE_groups, SCOPE_openid, SCOPE_profile]], User Attributes: [{at_hash=<HASH>, sub=<SUBJECT_ID>, zoneinfo=America/Los_Angeles, ver=1, email_verified=true, amr=[swk, mfa, pwd], iss=https://myorg.okta-emea.com/oauth2/<AUTHZ_SERVER_ID>, groups=[Developers], preferred_username=myuser@myorg.com, locale=en_US, given_name=My, nonce=<NONCE>, aud=[<AUDIENCE>], updated_at=2023-09-08T11:24:03Z, idp=00oiezc9clgcLMk1t0i6, auth_time=2023-09-12T15:07:26Z, name=My User, exp=2023-09-12T16:26:32Z, family_name=User, iat=2023-09-12T15:26:32Z, email=myuser@myorg.com, jti=<JTI>}], groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[OIDC_USER, SCOPE_email, SCOPE_groups, SCOPE_openid, SCOPE_profile]]]' in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@5c0ffc11'
2023-09-12 15:36:46,308 ERROR [parallel-2] r.c.p.Operators: Operator called default onErrorDropped
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.ClassCastException: Cannot cast com.provectus.kafka.ui.config.auth.RbacOidcUser to org.springframework.security.core.userdetails.UserDetails
Caused by: java.lang.ClassCastException: Cannot cast com.provectus.kafka.ui.config.auth.RbacOidcUser to org.springframework.security.core.userdetails.UserDetails

Additional context

It is worth noting that if I set subjects to be like this everything works fine and it correctly assigns the user ReadOnly permissions:

rbac:
  roles:
  - name: ReadOnly
    subjects:
    - provider: oauth
      type: email
      value: myuser@myorg.com   
Haarolean commented 1 year ago

@daviddyball hi, can I see the full log please? haven't you ommitted the stacktrace?

daviddyball commented 1 year ago

Apologies, it is a very long log and I wasn't sure if it'd be useful in its entirety:

ERROR [reactor-http-epoll-4] r.c.p.Operators: Operator called default onErrorDropped
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.ClassCastException: Cannot cast org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser to org.springframework.security.core.userdetails.UserDetails
Caused by: java.lang.ClassCastException: Cannot cast org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser to org.springframework.security.core.userdetails.UserDetails
        at java.base/java.lang.Class.cast(Class.java:3889)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
        at reactor.core.publisher.MonoCacheTime.subscribeOrReturn(MonoCacheTime.java:151)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4480)
        at reactor.core.publisher.Mono.subscribeWith(Mono.java:4561)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4323)
        at com.provectus.kafka.ui.service.audit.AuditService.audit(AuditService.java:186)
        at com.provectus.kafka.ui.controller.AbstractController.audit(AbstractController.java:30)
        at com.provectus.kafka.ui.controller.BrokersController.lambda$getBrokers$0(BrokersController.java:45)
        at reactor.core.publisher.FluxDoOnEach$DoOnEachSubscriber.onNext(FluxDoOnEach.java:164)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:292)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:236)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200)
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:292)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)
        at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:293)
        at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:474)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
        at reactor.core.publisher.MonoZip$ZipInner.onSubscribe(MonoZip.java:466)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoZip$ZipCoordinator.request(MonoZip.java:216)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onSubscribe(MonoIgnoreThen.java:134)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)
        at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:125)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:240)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189)
        at reactor.core.publisher.Operators.complete(Operators.java:137)
        at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:121)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:258)
        at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2305)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:338)
        at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:155)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onNext(FluxPeekFuseable.java:503)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
        at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:258)
        at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
        at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
        at reactor.core.publisher.MonoCacheTime.subscribeOrReturn(MonoCacheTime.java:151)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113)
        at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545)
        at reactor.core.publisher.FluxMap$MapConditionalSubscriber.request(FluxMap.java:295)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.request(FluxFilterFuseable.java:411)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
        at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onSubscribe(MonoFlatMap.java:291)
        at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2025)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onSubscribe(FluxFilterFuseable.java:305)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:85)
        at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onSubscribe(FluxMap.java:194)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onNext(FluxConcatMapNoPrefetch.java:206)
        at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerComplete(FluxConcatMapNoPrefetch.java:274)
        at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onComplete(FluxConcatMap.java:887)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189)
        at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:847)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:609)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:589)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.request(FluxFlatMap.java:347)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)
        at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2305)
        at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.request(FluxPeekFuseable.java:783)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)
        at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2305)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:338)
        at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108)
        at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.request(FluxPeekFuseable.java:437)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:85)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onSubscribe(FluxPeekFuseable.java:471)
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152)
        at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2025)
        at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:847)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:609)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:589)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:466)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onComplete(FluxPeekFuseable.java:277)
        at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:357)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2071)
        at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:134)
        at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
        at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:275)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1840)
        at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:337)
        at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:354)
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:258)
        at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863)
        at reactor.core.publisher.FluxConcatMap$WeakScalarSubscription.request(FluxConcatMap.java:479)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2305)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:338)
        at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70)
        at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.MonoCacheTime.subscribeOrReturn(MonoCacheTime.java:143)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:847)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:609)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:589)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:466)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onComplete(FluxPeekFuseable.java:277)
        at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:357)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:847)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:609)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:589)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:466)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onComplete(FluxPeekFuseable.java:277)
        at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:357)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144)
        at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:171)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onComplete(FluxPeekFuseable.java:595)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.request(FluxPeekFuseable.java:437)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onSubscribe(FluxPeekFuseable.java:471)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:171)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onComplete(FluxPeekFuseable.java:595)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.request(FluxPeekFuseable.java:437)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onSubscribe(FluxPeekFuseable.java:471)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
        at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2071)
        at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:118)
        at reactor.core.publisher.FluxIterable$IterableSubscription.fastPath(FluxIterable.java:424)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:291)
        at reactor.core.publisher.Operators$BaseFluxToMonoOperator.request(Operators.java:2041)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onSubscribe(MonoFlatMap.java:291)
        at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2025)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:81)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
        at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.drain(FluxFilterWhen.java:301)
        at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.onNext(FluxFilterWhen.java:140)
        at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
        at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.onSubscribe(FluxFilterWhen.java:200)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
        at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
        at reactor.netty.http.server.HttpServer$HttpServerHandle.onStateChange(HttpServer.java:1006)
        at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:710)
        at reactor.netty.transport.ServerTransport$ChildObserver.onStateChange(ServerTransport.java:481)
        at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:621)
        at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:113)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:230)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
        at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800)
        at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499)
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)
daviddyball commented 1 year ago

When I enabled TRACE on Jackson2JsonEncoder I can see the permissions it's assigning me after checking authorization:

2023-09-13 12:39:12,716 TRACE [reactor-http-epoll-2] o.s.h.c.j.Jackson2JsonEncoder: [7579b09d-11] Encoding [class AuthenticationInfoDTO {
    rbacEnabled: true
    userInfo: class UserInfoDTO {
        username: myuser@myorg.com
        permissions: []
    }
}]

So I guess it's not finding my group Developers and assigning me the intended ReadOnly role I created.

One thing I wasn't sure about was the SecurityContext that I posted above

[
  Authentication=OAuth2AuthenticationToken [
    Principal=RbacOidcUser[
      user=Name: [myuser@myorg.com],
      Granted Authorities: [
        [OIDC_USER, SCOPE_email, SCOPE_groups, SCOPE_openid, SCOPE_profile]
      ],
      User Attributes: [
        {
          at_hash=<HASH>,
          sub=<SUBJECT_ID>,
          zoneinfo=America/Los_Angeles,
          ver=1,
          email_verified=true,
          amr=[swk, mfa, pwd],
          iss=https://myorg.okta-emea.com/oauth2/<AUTHZ_SERVER_ID>,
          groups=[Developers],
          preferred_username=myuser@myorg.com,
          locale=en_US,
          given_name=My,
          nonce=<NONCE>,
          aud=[<AUDIENCE_ID>],
          updated_at=<DATE>,
          idp=<IDP_ID>,
          auth_time=<DATE>,
          name=My User,
          exp=<DATE>,
          family_name=User,
          iat=<DATE>,
          email=myuser@myorg.com,
          jti=<REDACTED>
        }
      ],
      groups=[]
    ],
    Credentials=[PROTECTED],
    Authenticated=true,
    Details=null,
    Granted Authorities=[OIDC_USER, SCOPE_email, SCOPE_groups, SCOPE_openid, SCOPE_profile]
  ]
]

It has two groups lists, one is inside the User Properties structure (which correctly has [Developers] listed in it) and the other is an attribute on the RbacOidcUser object (which is empty groups=[]). Is that expected?

Haarolean commented 1 year ago

Thanks, looks like a regression. Will fix shortly

gnsallman commented 11 months ago

Any update on a fix here? I'm running into the same issue with audit logging.

mmasipgu commented 10 months ago

Hi dear @Haarolean and @daviddyball ,

We are working now to implement OIDC like @daviddyball and having the same problem:

Our claim returns groups but RBAC doesn't map them

Found SecurityContext 'SecurityContextImpl [Authentication=OAuth2AuthenticationToken [Principal=RbacOAuth2User[user=Name: [*****], Granted Authorities: [[OAUTH2_USER, SCOPE_groups]], User Attributes: [{sub=**, aud=EBS-LAB-UI, iss=https://*****/oauth/, groups=[UI-ADMIN-NONPROD, UI-ADMIN-PROD], preferred_username=**, exp=1700314319, iat=1700299919}], groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[OAUTH2_USER, SCOPE_groups]]]' in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@6db19384'

As you can see groups in claim are groups=[UI-ADMIN-NONPROD, UI-ADMIN-PROD] But in mapping put groups=[]

Any idea how to fix that ?

Best Regards

mmasipgu commented 10 months ago

Hi dear @Haarolean and @daviddyball,

It was a "mistake from my side"

In that case when you use OAUTH in your rbac configuration under provider --> type need's to be role instaead of group to make the match between OAUTH CLAIM groups and KafkaUI groups.

rbac:
  roles:
    - name: "Admin Team"
      subjects:
      - provider: oauth
        type: role
        value: "UI-ADMIN-NONPROD"

Up and running as expected from my side :)

Best Regards

daviddyball commented 9 months ago

@mmasipgu Are you using Okta? I tried switching out type: group for type: role, but I still get the Cannot cast DefaultOidcUser to UserDetails error 🤔

HenkvanDyk commented 8 months ago

@daviddyball I encountered the exact same issue. Resolved using this:

auth:
  type: "OAUTH2"
  oauth2:
    client:
      okta:
        scope:
          - openid
          - profile
          - email
          - groups
        client-name: Okta
        provider: okta
        redirect-uri: http://localhost:8082/login/oauth2/code/okta
        authorization-grant-type: authorization_code
        issuer-uri: https://<my-okta>.okta.com
        authorization-uri: https://<my-okta>.okta.com/oauth2/v1/authorize
        token-uri: https://<my-okta>.okta.com/oauth2/v1/token
        user-info-uri: https://<my-okta>.okta.com/oauth2/v1/userinfo
        jwk-set-uri: https://<my-okta>.okta.com/oauth2/v1/keys
        user-name-attribute: name
        custom-params:
          type: oauth
          roles-field: groups

rbac:
  roles:
    - name: "Read Only"
      clusters:
        - <cluster_name>
      subjects:
        - provider: oauth
          type: role
          value: "<group value>"
      permissions:
        - resource: clusterconfig
          actions: [ "view" ]

My okta assignments allow API Scopes include:

janario commented 5 months ago

Any updates on this one?

I just had a similar issue with google auth

Haarolean commented 5 months ago

@janario hey, this repo is not maintained (#4255). Will be happy to see your feedback here: https://github.com/kafbat/kafka-ui . I'm pretty sure RBAC with generic oauth providers (like okta) works, so if you have any issues -- feel free to either raise a discussion or join us on discord.