Loading our SiteSetting properties before plugin loads

I’m aware that after_initialize allows us to have access to the loaded properties in SiteSetting but I’m struggling to update my register_middleware method.

Since ruby isn’t my working language it’s more difficult for me to follow.

So in lib/plugin/instance.rb I see this may be responsible for handling the loading of our plugins.

But simplying wrapping my class in after_initialize doend doesn’t seem to work. (Nothing appears to have loaded at all if I do that).

  def register_middleware(omniauth)
    omniauth.provider :steam, SiteSetting.discourse_steam_login_api_key || ENV['STEAM_WEB_API_KEY']
  end

Is there anything obvious I’m missing?

The problem essentially is that - when register_middleware is called, I don’t think my SiteSetting property is yet loaded.

I’ve set the enabled_site_settings at the top of the file:

enabled_site_setting :discourse_steam_login_enabled
enabled_site_setting :discourse_steam_login_api_key

Some plugins only use their enabled flag, others add more properties this way. So it’s a little confusing what the purpose of enabled_site_setting is, some clarification on that as well if possible.

I’m hoping this is something relatively simple I’ve overlooked :sleepy:

The site settings you use are defined in ./config/site_settings.yml.

The enabled_site_setting tells the /admin/plugins page which site setting controls “this plugin is turned on”. This is needed for multisite setups.

1 Like

Hi guys, I’ve uploaded an experimental branch of my plugin in case anyone is able to see what the problem is. The issue is with register_middleware in plugin.rb.

I can’t get access to the SiteSetting variable.

If I do after_initialize { p SiteSetting.discourse_steam_login_api_key } I can see it. But I don’t know how to weave that into register_middleware appropriately.

2 Likes

@eviltrout, I’m having the same issue. I can’t get SiteSetting at a point where it contains the values. If I substitute the SiteSetting variable with a hard-coded string or to use ENV it works perfectly. Is there something @def and I need to do to get SiteSetting to be available here?

More info:

/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/logster-1.2.4/lib/logster/logger.rb:74:in `add_with_opts'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/logster-1.2.4/lib/logster/logger.rb:35:in `add'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/logger.rb:434:in `info'
/home/discourse/discourse/plugins/discourse-plugin-oauth-linkedin/auth/linkedin_authenticator.rb:28:in `register_middleware'
/home/discourse/discourse/config/initializers/009-omniauth.rb:10:in `block (2 levels) in <top (required)>'
/home/discourse/discourse/config/initializers/009-omniauth.rb:9:in `each'
/home/discourse/discourse/config/initializers/009-omniauth.rb:9:in `block in <top (required)>'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rack-1.6.4/lib/rack/builder.rb:55:in `instance_eval'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rack-1.6.4/lib/rack/builder.rb:55:in `initialize'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/omniauth-1.3.1/lib/omniauth/builder.rb:6:in `initialize'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/stack.rb:43:in `new'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/stack.rb:43:in `build'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/stack.rb:118:in `block in build'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/stack.rb:118:in `each'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/stack.rb:118:in `inject'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/stack.rb:118:in `build'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/engine.rb:502:in `app'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/application/finisher.rb:34:in `block in <module:Finisher>'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/initializable.rb:30:in `instance_exec'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/initializable.rb:30:in `run'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/initializable.rb:55:in `block in run_initializers'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:226:in `block in tsort_each'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:348:in `block (2 levels) in each_strongly_connected_component'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:427:in `each_strongly_connected_component_from'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:347:in `block in each_strongly_connected_component'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:345:in `each'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:345:in `call'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:345:in `each_strongly_connected_component'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:224:in `tsort_each'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:205:in `tsort_each'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/initializable.rb:54:in `run_initializers'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/application.rb:352:in `initialize!'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/railtie.rb:194:in `public_send'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.2.6/lib/rails/railtie.rb:194:in `method_missing'
/home/discourse/discourse/config/environment.rb:5:in `<top (required)>'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:274:in `require'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:274:in `block in require'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:240:in `load_dependency'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:274:in `require'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/sidekiq-4.1.2/lib/sidekiq/cli.rb:234:in `boot_system'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/sidekiq-4.1.2/lib/sidekiq/cli.rb:50:in `run'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/sidekiq-4.1.2/bin/sidekiq:12:in `<top (required)>'
/home/discourse/.rbenv/versions/2.1.2/bin/sidekiq:23:in `load'
/home/discourse/.rbenv/versions/2.1.2/bin/sidekiq:23:in `<top (required)>'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/cli/exec.rb:63:in `load'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/cli/exec.rb:63:in `kernel_load'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/cli/exec.rb:24:in `run'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/cli.rb:304:in `exec'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor.rb:359:in `dispatch'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor/base.rb:440:in `start'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/cli.rb:11:in `start'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/exe/bundle:27:in `block in <top (required)>'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/lib/bundler/friendly_errors.rb:98:in `with_friendly_errors'
/home/discourse/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bundler-1.12.3/exe/bundle:19:in `<top (required)>'
/home/discourse/.rbenv/versions/2.1.2/bin/bundle:23:in `load'
/home/discourse/.rbenv/versions/2.1.2/bin/bundle:23:in `<main>'
3 Likes

Fixed in

https://github.com/discourse/discourse/commit/0ffe8402a9f2f1a7cc4ced1abf1f21fd8f93578b

6 Likes

Okay, so it seems this may not be the best place to use SiteSetting and might be best to stick to ENV, as now it is capable of reading the SiteSetting, but because it is registered when Discourse starts, it doesn’t and potentially can’t, pick up on changes to the SiteSettings it refers to without stopping and starting Discourse again.

My Steps that seem to indicate this

  • Installed an oauth plugin using SiteSetting inside register_middleware
  • Update SiteSettings
  • Attempt login using ‘with Authenicator’
  • Get OAuth Bad Parameters error
  • Stop/Start Discourse
  • Attempt login
  • Success!

So unless there is a way to de-register and re-register the middleware, I’m not sure storing this data in SiteSetting is going to help anything. @def, are you seeing similar results?

1 Like

I haven’t had time to test the changes but from what you’re saying I’m going to assume it will be exactly the same situation for me. Are we implementing this the right way? There’s a lot of other authentication plugins in the mix. I’m wondering if we’re just doing it wrong

Not 100% sure, but apart from implementing its own strategy, this plugin written by the Discourse Team is attempting to use Saleseforce SiteSettings in its register_middleware

https://github.com/discourse/discourse-salesforce-auth/blob/master/plugin.rb

Is there something about the use of a lambda that preserves the reference to the SiteSetting then(?)

Example from facebook_authenticator.rb:

class Auth::FacebookAuthenticator < Auth::Authenticator
      def register_middleware(omniauth)
        omniauth.provider :facebook,
               :setup => lambda { |env|
                  strategy = env["omniauth.strategy"]
                  strategy.options[:client_id] = SiteSetting.facebook_app_id
                  strategy.options[:client_secret] = SiteSetting.facebook_app_secret
               },
               :scope => "email"
      end
end

Yeah, actually that does remedy the problem. It does require you to use OAuth2 though (or so it seems), Using OAuth (version 1) doesn’t seem to honor setting options that way. At least I seem to have mine working now properly (still doing some more testing).

1 Like

Is your code some where I can look at?

Yeah, it is at https://github.com/cpradio/discourse-plugin-linkedin-auth (or at least the final version is now there)

Without using the lambda, it doesn’t reload the SiteSettings without a server restart. So if you replace the lambda with

SiteSetting.linkedin_client_id,
SiteSetting.linkedin_secret

You have to restart the app to get it to take the changes (or so it seems) – just confirmed it on my dev machine.

I imagine this is due the lambda being processed at actual runtime later on when the button is actually physically pressed, versus assigned at startup, but this is purely a hunch.

1 Like

Yup that is the right behavior. Settings configured in the initializers will require an app reload to be updated.

2 Likes

Cool, that is what I suspected. Figured the lambda expression must be delaying the use of the settings to closer to actual run time instead of when the script was started. :slight_smile:

Thanks for bringing more attention to this topic @cpradio and thank you for taking a look into this as well @tgxworld, I’ve fixed my plugin now by using a lambda. I made it work with the OmniAuth 1.0 strategy I depend on by modifying it slightly so that it .calls the lambda, and hey presto - the reference to the site setting is preserved.

5 Likes