From 221c233e6d8c9da12b3b9c6356be54a21b01ec25 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Sat, 11 May 2019 16:53:37 +0100 Subject: [PATCH 1/5] first commit --- jobs/refresh_api_access_token.rb | 7 +++ lib/authorization.rb | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 jobs/refresh_api_access_token.rb create mode 100644 lib/authorization.rb diff --git a/jobs/refresh_api_access_token.rb b/jobs/refresh_api_access_token.rb new file mode 100644 index 00000000..08503361 --- /dev/null +++ b/jobs/refresh_api_access_token.rb @@ -0,0 +1,7 @@ +module Jobs + class RefreshAPIAccessToken < Jobs::Base + def execute(args) + CustomWizard::Authorization.refresh_access_token + end + end +end diff --git a/lib/authorization.rb b/lib/authorization.rb new file mode 100644 index 00000000..e96df6f9 --- /dev/null +++ b/lib/authorization.rb @@ -0,0 +1,91 @@ +require 'excon' + +class CustomWizard::Authorization + def self.access_token + PluginStore.get('custom_wizard', 'access_token') || {} + end + + def self.set_access_token(data) + PluginStore.set('custom_wizard', 'access_token', data) + end + + def self.refresh_token + PluginStore.get('custom_wizard', 'refresh_token') + end + + def self.set_refresh_token(token) + PluginStore.set('custom_wizard', 'refresh_token', token) + end + + def self.code + PluginStore.get('custom_wizard', 'code') + end + + def self.set_code(code) + PluginStore.set('custom_wizard', 'code', code) + end + + def self.get_access_token + body = { + client_id: SiteSetting.custom_wizard_client_id, + client_secret: SiteSetting.custom_wizard_client_secret, + code: CustomWizard::Authorization.code, + grant_type: 'authorization_code', + redirect_uri: (Rails.env.development? ? CustomWizard::NGROK_URL : Discourse.base_url) + '/custom_wizard/authorization/callback' + } + + result = Excon.post( + "https://api.custom_wizard.com/token", + :headers => { + "Content-Type" => "application/x-www-form-urlencoded" + }, + :body => URI.encode_www_form(body) + ) + + self.handle_token_result(result) + end + + def self.refresh_access_token + body = { + grant_type: 'refresh_token', + refresh_token: CustomWizard::Authorization.refresh_token + } + + authorization_string = SiteSetting.custom_wizard_client_id + ':' + SiteSetting.custom_wizard_client_secret + + result = Excon.post( + "https://api.custom_wizard.com/token", + :headers => { + "Content-Type" => "application/x-www-form-urlencoded", + "Authorization" => "Basic #{Base64.strict_encode64(authorization_string)}" + }, + :body => URI.encode_www_form(body) + ) + + self.handle_token_result(result) + end + + def self.handle_token_result(result) + data = JSON.parse(result.body) + return false if (data['error']) + + token = data['access_token'] + expires_at = Time.now + data['expires_in'].seconds + refresh_at = expires_at.to_time - 2.hours + + Jobs.enqueue_at(refresh_at, :refresh_custom_wizard_access_token) + + CustomWizard::Authorization.set_access_token( + token: token, + expires_at: expires_at, + refresh_at: refresh_at + ) + + CustomWizard::Authorization.set_refresh_token(data['refresh_token']) + end + + def self.authorized + CustomWizard::Authorization.access_token[:token] && + CustomWizard::Authorization.access_token[:expires_at].to_datetime > Time.now + end +end From 839f085500f1c5ba7af11f59258bd7310394ef11 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Sun, 12 May 2019 15:08:59 +0100 Subject: [PATCH 2/5] add file reference to plugin dot rb --- plugin.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin.rb b/plugin.rb index 231bb7e1..c7268648 100644 --- a/plugin.rb +++ b/plugin.rb @@ -77,6 +77,7 @@ after_initialize do load File.expand_path('../lib/template.rb', __FILE__) load File.expand_path('../lib/wizard.rb', __FILE__) load File.expand_path('../lib/wizard_edits.rb', __FILE__) + load File.expand_path('../lib/authorization.rb', __FILE__) load File.expand_path('../controllers/wizard.rb', __FILE__) load File.expand_path('../controllers/steps.rb', __FILE__) load File.expand_path('../controllers/admin.rb', __FILE__) From 19a9497d74c9ca8ea1a983570bd43462f0b23b74 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Tue, 14 May 2019 14:54:33 +0100 Subject: [PATCH 3/5] added authentication protocol type --- lib/authorization.rb | 94 +++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/lib/authorization.rb b/lib/authorization.rb index e96df6f9..4a6e8642 100644 --- a/lib/authorization.rb +++ b/lib/authorization.rb @@ -1,60 +1,97 @@ require 'excon' class CustomWizard::Authorization - def self.access_token - PluginStore.get('custom_wizard', 'access_token') || {} + + BASIC_AUTH = 'basic_authentication' + OAUTH2_AUTH = 'OAuth2_authentication' + + def self.authentication_protocol(service) + PluginStore.get(service, 'authentication_protocol') || {} end - def self.set_access_token(data) - PluginStore.set('custom_wizard', 'access_token', data) + def self.set_authentication_protocol(service, protocol) + raise Discourse::InvalidParameters unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol + PluginStore.set(service, 'authentication_protocol', protocol) end - def self.refresh_token - PluginStore.get('custom_wizard', 'refresh_token') + def self.access_token(service) + PluginStore.get(service, 'access_token') || {} end - def self.set_refresh_token(token) - PluginStore.set('custom_wizard', 'refresh_token', token) + def self.set_access_token(service, data) + PluginStore.set(service, 'access_token', data) end - def self.code - PluginStore.get('custom_wizard', 'code') + def self.refresh_token (service) + PluginStore.get(service, 'refresh_token') end - def self.set_code(code) - PluginStore.set('custom_wizard', 'code', code) + def self.set_refresh_token(service, token) + PluginStore.set(service, 'refresh_token', token) end - def self.get_access_token + def self.code(service) + PluginStore.get(service,'code') + end + + def self.set_code(service, code) + PluginStore.set(service, 'code', code) + end + + def self.client_id(service) + PluginStore.get(service,'client_id') + end + + def self.set_client_id(service, client_id) + PluginStore.set(service, 'client_id', client_id) + end + + def self.client_secret(service) + PluginStore.get(service,'client_secret') + end + + def self.set_client_secret(service, client_secret) + PluginStore.set(service, 'client_secret', client_secret) + end + + def self.url(service) + PluginStore.get(service,'url') + end + + def self.set_url(service, url) + PluginStore.set(service, 'url', url) + end + + def self.get_access_token(service) body = { - client_id: SiteSetting.custom_wizard_client_id, - client_secret: SiteSetting.custom_wizard_client_secret, - code: CustomWizard::Authorization.code, + client_id: CustomWizard::Authorization.client_id(service), + client_secret: CustomWizard::Authorization.client_secret(service), + code: CustomWizard::Authorization.code(service), grant_type: 'authorization_code', redirect_uri: (Rails.env.development? ? CustomWizard::NGROK_URL : Discourse.base_url) + '/custom_wizard/authorization/callback' } result = Excon.post( - "https://api.custom_wizard.com/token", + CustomWizard::Authorization.url(service), :headers => { "Content-Type" => "application/x-www-form-urlencoded" }, :body => URI.encode_www_form(body) ) - self.handle_token_result(result) + self.handle_token_result(service, result) end - def self.refresh_access_token + def self.refresh_access_token(service) body = { grant_type: 'refresh_token', refresh_token: CustomWizard::Authorization.refresh_token } - authorization_string = SiteSetting.custom_wizard_client_id + ':' + SiteSetting.custom_wizard_client_secret + authorization_string = CustomWizard::Authorization.client_id(service) + ':' + CustomWizard::Authorization.client_secret(service) result = Excon.post( - "https://api.custom_wizard.com/token", + CustomWizard::Authorization.url(service), :headers => { "Content-Type" => "application/x-www-form-urlencoded", "Authorization" => "Basic #{Base64.strict_encode64(authorization_string)}" @@ -62,10 +99,10 @@ class CustomWizard::Authorization :body => URI.encode_www_form(body) ) - self.handle_token_result(result) + self.handle_token_result(service, result) end - def self.handle_token_result(result) + def self.handle_token_result(service, result) data = JSON.parse(result.body) return false if (data['error']) @@ -73,19 +110,20 @@ class CustomWizard::Authorization expires_at = Time.now + data['expires_in'].seconds refresh_at = expires_at.to_time - 2.hours - Jobs.enqueue_at(refresh_at, :refresh_custom_wizard_access_token) + Jobs.enqueue_at(refresh_at, :refresh_api_access_token) CustomWizard::Authorization.set_access_token( + service: service, token: token, expires_at: expires_at, refresh_at: refresh_at ) - CustomWizard::Authorization.set_refresh_token(data['refresh_token']) + CustomWizard::Authorization.set_refresh_token(service, data['refresh_token']) end - def self.authorized - CustomWizard::Authorization.access_token[:token] && - CustomWizard::Authorization.access_token[:expires_at].to_datetime > Time.now + def self.authorized(service) + CustomWizard::Authorization.access_token[service, :token] && + CustomWizard::Authorization.access_token[service, :expires_at].to_datetime > Time.now end end From 08172d7d3531b1dbe37e92028eac9762f1963ecd Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Wed, 15 May 2019 07:54:32 +0100 Subject: [PATCH 4/5] added basic authentication and error handling --- lib/authorization.rb | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/authorization.rb b/lib/authorization.rb index 4a6e8642..8b5093d6 100644 --- a/lib/authorization.rb +++ b/lib/authorization.rb @@ -10,7 +10,8 @@ class CustomWizard::Authorization end def self.set_authentication_protocol(service, protocol) - raise Discourse::InvalidParameters unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol + # TODO: make error more informative + raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol PluginStore.set(service, 'authentication_protocol', protocol) end @@ -38,6 +39,22 @@ class CustomWizard::Authorization PluginStore.set(service, 'code', code) end + def self.username(service) + PluginStore.get(service,'username') + end + + def self.set_username(service, username) + PluginStore.set(service, 'username', username) + end + + def self.password(service) + PluginStore.get(service,'password') + end + + def self.set_password(service, password) + PluginStore.set(service, 'password', password) + end + def self.client_id(service) PluginStore.get(service,'client_id') end @@ -62,6 +79,28 @@ class CustomWizard::Authorization PluginStore.set(service, 'url', url) end + def self.get_header_authorization_string(service) + # TODO: make error more informative, raise error if service not defined + protocol = authentication_protocol(service) + raise Discourse::InvalidParameters.new(:service) unless protocol.present? + raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol + + if protocol = BASIC_AUTH + # TODO: improve error reporting + username = username(service) + raise Discourse::InvalidParameters.new(:username) unless username.present? + password = password(service) + raise Discourse::InvalidParameters.new(:password) unless password.present? + authorization_string = (username + ":" + password).chomp + "Basic #{Base64.strict_encode64(authorization_string)}" + else + # must be OAUTH2 + # TODO: make error more informative, raise error if there is no recorded access token + raise Discourse::InvalidParameters unless access_token[:token].present? + "Bearer #{access_token[:token]}" + end + end + def self.get_access_token(service) body = { client_id: CustomWizard::Authorization.client_id(service), From 0ca8758f6ceec87872fcf187cd1a4a763d481e03 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Wed, 15 May 2019 08:09:48 +0100 Subject: [PATCH 5/5] comments cleanup and added better paramater check for oauth2 --- lib/authorization.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/authorization.rb b/lib/authorization.rb index 8b5093d6..c18785ab 100644 --- a/lib/authorization.rb +++ b/lib/authorization.rb @@ -10,7 +10,6 @@ class CustomWizard::Authorization end def self.set_authentication_protocol(service, protocol) - # TODO: make error more informative raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol PluginStore.set(service, 'authentication_protocol', protocol) end @@ -80,13 +79,11 @@ class CustomWizard::Authorization end def self.get_header_authorization_string(service) - # TODO: make error more informative, raise error if service not defined protocol = authentication_protocol(service) raise Discourse::InvalidParameters.new(:service) unless protocol.present? raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol if protocol = BASIC_AUTH - # TODO: improve error reporting username = username(service) raise Discourse::InvalidParameters.new(:username) unless username.present? password = password(service) @@ -94,10 +91,10 @@ class CustomWizard::Authorization authorization_string = (username + ":" + password).chomp "Basic #{Base64.strict_encode64(authorization_string)}" else - # must be OAUTH2 - # TODO: make error more informative, raise error if there is no recorded access token - raise Discourse::InvalidParameters unless access_token[:token].present? - "Bearer #{access_token[:token]}" + # must be OAUTH2 + access_token = access_token(service) + raise Discourse::InvalidParameters.new(access_token) unless access_token.present? + "Bearer #{access_token}" end end