kafka-ui icon indicating copy to clipboard operation
kafka-ui copied to clipboard

OIDC/oAuth2 + Okta Groups Extraction

Open daviddyball opened this issue 1 year ago • 11 comments

Issue submitter TODO list

  • [X] I've looked up my issue in FAQ
  • [X] I've searched for an already existing issues here
  • [X] I've tried running master-labeled docker image and the issue still persists there
  • [X] I'm running a supported version of the application which is listed here

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: [[email protected]],
      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],
          [email protected],
          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 protected],
          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: [[email protected]], 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], [email protected], 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 protected], 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: [email protected]   

daviddyball avatar Sep 12 '23 15:09 daviddyball

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

Haarolean avatar Sep 13 '23 10:09 Haarolean

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 avatar Sep 13 '23 11:09 daviddyball

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: [email protected]
        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: [[email protected]],
      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],
          [email protected],
          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 protected],
          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?

daviddyball avatar Sep 13 '23 12:09 daviddyball

Thanks, looks like a regression. Will fix shortly

Haarolean avatar Sep 13 '23 14:09 Haarolean

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

gnsallman avatar Oct 04 '23 18:10 gnsallman

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 avatar Nov 18 '23 09:11 mmasipgu

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

mmasipgu avatar Nov 21 '23 16:11 mmasipgu

@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 🤔

daviddyball avatar Nov 30 '23 10:11 daviddyball

@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:

  • okta.groups.read
  • okta.roles.read
  • okta.users.read

HenkvanDyk avatar Jan 02 '24 17:01 HenkvanDyk

Any updates on this one?

I just had a similar issue with google auth

janario avatar Apr 18 '24 14:04 janario

@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.

Haarolean avatar Apr 18 '24 14:04 Haarolean