Skip to content

Conversation

wozniakjan
Copy link
Member

followup to #6848, no changelog update since the refactor has not been released yet and this essentially continues in that same entry.

Checklist

Relates to #5797,

Copy link

github-actions bot commented Aug 8, 2025

Thank you for your contribution! 🙏

Please understand that we will do our best to review your PR and give you feedback as soon as possible, but please bear with us if it takes a little longer as expected.

While you are waiting, make sure to:

  • Add an entry in our changelog in alphabetical order and link related issue
  • Update the documentation, if needed
  • Add unit & e2e tests for your changes
  • GitHub checks are passing
  • Is the DCO check failing? Here is how you can fix DCO issues

Once the initial tests are successful, a KEDA member will ensure that the e2e tests are run. Once the e2e tests have been successfully completed, the PR may be merged at a later date. Please be patient.

Learn more about our contribution guide.

@wozniakjan wozniakjan force-pushed the pulsar_auth_refactor branch from ca74f79 to 439a0a7 Compare August 8, 2025 08:53
Comment on lines +126 to +139
if m.PulsarAuth.ClientSecret == "" {
// client_secret is not required for mtls OAuth(RFC8705)
// set secret to random string to work around the Go OAuth lib
m.PulsarAuth.ClientSecret = time.Now().String()
}

if auth != nil && auth.EnableOAuth {
if err := m.configureOAuth(auth); err != nil {
return err
var scopes []string
for _, scope := range m.PulsarAuth.Scopes {
if strings.TrimSpace(scope) != "" {
scopes = append(scopes, scope)
}
}

m.pulsarAuth = auth
return nil
}

// configureOAuth configures OAuth settings
func (m *pulsarMetadata) configureOAuth(auth *authentication.AuthMeta) error {
if auth.OauthTokenURI == "" {
auth.OauthTokenURI = m.OauthTokenURI
}
if auth.Scopes == nil {
auth.Scopes = authentication.ParseScope(m.Scope)
}
if auth.ClientID == "" {
auth.ClientID = m.ClientID
m.PulsarAuth.Scopes = scopes
if len(m.PulsarAuth.Scopes) == 0 {
m.PulsarAuth.Scopes = nil
Copy link
Member Author

@wozniakjan wozniakjan Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this post processing has reduced a bit thanks to using authentication.Config instead of authentication.AuthMeta but there are still some tweaks necessary because of how pulsar used to treat certain corner cases

Comment on lines +82 to +86
OauthTokenURI string `keda:"name=oauthTokenURI, order=authParams;triggerMetadata"`
Scopes []string `keda:"name=scopes;scope, order=authParams;triggerMetadata"`
ClientID string `keda:"name=clientID, order=authParams;triggerMetadata"`
ClientSecret string `keda:"name=clientSecret, order=authParams"`
EndpointParams url.Values `keda:"name=endpointParams, order=authParams"`
EndpointParams url.Values `keda:"name=endpointParams, order=authParams;triggerMetadata"`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pulsar is the first refactored scaler that allows setting these in trigger metadata, I guess apart from ClientSecret, it might be technically ok from a security perspective?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All those values are not sensitive, so it's fine in terms of security if we get them from metadata

Comment on lines +147 to +148
if c.EnabledOAuth() && (c.OauthTokenURI == "" || c.ClientID == "") {
return fmt.Errorf("oauthTokenURI=%v and clientID=%v are required when oauth is enabled", empty(c.OauthTokenURI), empty(c.ClientID))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks to pulsar, I learned that ClientSecret is actually not a compulsory parameter when setting oauth

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are confidential and public clients defined -> https://oauth.net/2/client-types/
Said this, public clients are to identify users and not to retrieve tokens on behalf of themselves (for example, the typical cli that needs to login a user). I'm wondering which is the use case for a public clients in this case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking the scaler code, it looks as it's because clients can be identified on pulsar also by mTLS (it's another client auth allowed by oauth). In this case, it makes sense to verify something like, clientSecrent OR mTLS

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if c.EnabledOAuth() && (c.OauthTokenURI == "" || c.ClientID == "" || (!c.EnabledTLS() && c.ClientSecret == "")) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, thanks for the explanation!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are tests that configure only authModes: oauth with oauthTokenURI, clientID, no clientSecret and then only ca. That means technically TLS is not fully configured and enabled, only the ca is set. Should that be also valid?

details

{map[string]string{"adminURL": "http://172.20.0.151:80", "topic": "persistent://public/default/my-topic", "subscription": "sub1", "authModes": "oauth", "oauthTokenURI": "https2", "scope": "scope2", "clientID": "id2"}, map[string]string{"ca": "cadata", "oauthTokenURI": "", "scope": "", "clientID": "", "clientSecret": ""}, false, false, "", "", "cadata", "", "", "", false, "https2", "scope2", "id2", "", "", nil},

triggerMetadata:
  adminURL: "http://172.20.0.151:80"
  authModes: oauth
  clientID: id2
  oauthTokenURI: https2
  scope: scope2
  subscription: sub1
  topic: persistent://public/default/my-topic
authParams:
  ca: "cadata"
  clientID: ""
  clientSecret: ""
  oauthTokenURI: ""
  scope: ""

{map[string]string{"adminURL": "http://172.20.0.151:80", "topic": "persistent://public/default/my-topic", "subscription": "sub1", "authModes": "oauth", "oauthTokenURI": "https4", "scope": " sc:scope2, \tsc:scope1 ", "clientID": "id4"}, map[string]string{"ca": "cadata", "oauthTokenURI": "", "scope": "", "clientID": "", "clientSecret": ""}, false, false, "", "", "cadata", "", "", "", false, "https4", "sc:scope1 sc:scope2", "id4", "", "", nil},

triggerMetadata:
  adminURL: "http://172.20.0.151:80"
  topic: "persistent://public/default/my-topic"
  subscription: "sub1"
  authModes: "oauth"
  oauthTokenURI: "https4"
  scope: "  sc:scope2,tsc:scope1"
  clientID: "id4"
authParams:
  ca: "cadata"
  oauthTokenURI: ""
  scope: ""
  clientID: ""
  clientSecret": ""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me check the SDK under the hood, but my first thought is that this isn't safe at all xD

Copy link
Member

@JorTurFer JorTurFer Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pfff..... the current implementation relies on golang.org/x/oauth2/clientcredentials and it uses client_credentials flow to retrieve a token on behalf of the client itself just with clientID and clientSecret (no other auth methods are used), so IMHO the secret must be mandatory in terms of security here.
ClientID without secret in this context means that whoever knows the ClientID (which is not a secret by definition) can call to pulsar as an authentication client.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making the clientSecret mandatory is a breaking change on our side (nevertheless, I'd expect this requirement by pulsar server) so maybe we can raise a warning when it's not set, WDTY?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, so I will allow this in code to not introduce any breaking changes but log a warning. Thanks for checking!

@wozniakjan
Copy link
Member Author

wozniakjan commented Aug 8, 2025

/run-e2e
Update: You can check the progress here

@wozniakjan wozniakjan requested a review from Copilot August 8, 2025 08:59
@wozniakjan wozniakjan marked this pull request as ready for review August 8, 2025 08:59
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the Pulsar scaler authentication system to use a new structured authentication configuration approach. It replaces the previous pulsarAuth field with PulsarAuth and changes from direct field access to method-based access for authentication states.

  • Refactored authentication field from pulsarAuth to PulsarAuth with new type *authentication.Config
  • Updated authentication state checks to use method calls instead of direct field access
  • Enhanced error messages in authentication validation with more descriptive formatting

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
pkg/scalers/pulsar_scaler.go Main refactor replacing authentication struct and implementation methods
pkg/scalers/pulsar_scaler_test.go Updated test assertions to use new authentication API methods
pkg/scalers/authentication/authentication_types.go Enhanced OAuth field tags and improved validation error messages

@wozniakjan wozniakjan requested review from a team and rickbrouwer August 8, 2025 09:04
@rickbrouwer
Copy link
Member

First very minor feedback is that the commit/pr title contains a spelling error ;)

@wozniakjan wozniakjan changed the title refactor puslar scaler auth refactor pulsar scaler auth Aug 8, 2025
@wozniakjan
Copy link
Member Author

First very minor feedback is that the commit/pr title contains a spelling error ;)

thanks! so I guess copilot doesn't check the PR titles...

@wozniakjan wozniakjan marked this pull request as draft August 8, 2025 10:55
Signed-off-by: Jan Wozniak <[email protected]>
@wozniakjan wozniakjan force-pushed the pulsar_auth_refactor branch from 439a0a7 to 73e8bd5 Compare August 8, 2025 11:37
@wozniakjan
Copy link
Member Author

wozniakjan commented Aug 8, 2025

/run-e2e pulsar
Update: You can check the progress here

func (m *pulsarMetadata) handleBackwardsCompatibility(config *scalersconfig.ScalerConfig) {
// For backwards compatibility, we need to map "tls: enable" to auth modes
if m.TLS == enable && (config.AuthParams["cert"] != "" || config.AuthParams["key"] != "") {
if config.TriggerMetadata["tls"] == enable && (config.AuthParams["cert"] != "" || config.AuthParams["key"] != "") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this part be moved to validate()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a fair point, but I'm not sure putting it to Validate() would be the right thing.

This section is a bit odd, the scaler has supported setting TLS in a way that deviates from standard settings implemented with other scalers. I don't want to break that because I am sure some users expect this behavior, but putting this to Validate() would likely mean we don't deprecate this ever. Right now, I don't know enough about this scaler to bravely mark this for deprecation but I'm going to add a note for later inspection and hopefully we unite TLS and auth params configuration for Pulsar scaler to match other scalers.

@rickbrouwer
Copy link
Member

Hi @wozniakjan

This PR is still in draft. Is this the correct status, or is it actually already ready for review?

@wozniakjan
Copy link
Member Author

This PR is still in draft. Is this the correct status, or is it actually already ready for review?

still a draft, need to incorporate feedback from #6969 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants