From 23bb04566d303bafefc583750f8cea66106b9ff0 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 17 Dec 2015 12:49:40 +0100 Subject: [PATCH] Init backend of generic external credentials. --- Gemfile | 1 - Gemfile.lock | 7 +- .../external_credentials_controller.rb | 75 ++++++++++ ...external_credentials_twitter_controller.rb | 129 ------------------ app/models/external_credential.rb | 26 +++- config/routes/external_credentials.rb | 15 ++ config/routes/external_credentials_twitter.rb | 14 -- lib/auto_wizard.rb | 1 + lib/external_credential/twitter.rb | 66 +++++++++ 9 files changed, 183 insertions(+), 151 deletions(-) create mode 100644 app/controllers/external_credentials_controller.rb delete mode 100644 app/controllers/external_credentials_twitter_controller.rb create mode 100644 config/routes/external_credentials.rb delete mode 100644 config/routes/external_credentials_twitter.rb create mode 100644 lib/external_credential/twitter.rb diff --git a/Gemfile b/Gemfile index bfd5de4ce..798f85deb 100644 --- a/Gemfile +++ b/Gemfile @@ -33,7 +33,6 @@ gem 'omniauth-linkedin' gem 'omniauth-google-oauth2' gem 'twitter' -gem 'twitter_oauth' gem 'koala' gem 'mail', '~> 2.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 284941144..f2c446bfa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -275,7 +275,7 @@ GEM simplecov-rcov (0.2.3) simplecov (>= 0.4.1) slop (3.6.0) - spring (1.5.0) + spring (1.6.0) sprockets (3.5.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -307,10 +307,6 @@ GEM memoizable (~> 0.4.0) naught (~> 1.0) simple_oauth (~> 0.3.0) - twitter_oauth (0.4.94) - json (>= 1.8.0) - mime-types (>= 1.16) - oauth (>= 0.4.7) tzinfo (1.2.2) thread_safe (~> 0.1) uglifier (2.7.2) @@ -373,7 +369,6 @@ DEPENDENCIES test-unit therubyracer twitter - twitter_oauth uglifier writeexcel diff --git a/app/controllers/external_credentials_controller.rb b/app/controllers/external_credentials_controller.rb new file mode 100644 index 000000000..17cbc32e7 --- /dev/null +++ b/app/controllers/external_credentials_controller.rb @@ -0,0 +1,75 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +class ExternalCredentialsController < ApplicationController + before_action :authentication_check + + def index + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_index_render(ExternalCredential, params) + end + + def show + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_show_render(ExternalCredential, params) + end + + def create + return if deny_if_not_role(Z_ROLENAME_ADMIN) + + # try access + begin + attributes = ExternalCredential.app_verify(params) + model_create_render(ExternalCredential, { name: params[:provider].downcase, credentials: attributes }) + return + rescue => e + render json: { error: e.message }, status: :unprocessable_entity + end + end + + def update + return if deny_if_not_role(Z_ROLENAME_ADMIN) + + # try access + begin + attributes = ExternalCredential.app_verify(params) + model_update_render(ExternalCredential, { name: params[:provider].downcase, credentials: attributes }) + return + rescue => e + render json: { error: e.message }, status: :unprocessable_entity + end + end + + def destroy + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_destory_render(ExternalCredential, params) + end + + def link_account + return if deny_if_not_role(Z_ROLENAME_ADMIN) + provider = params[:provider].downcase + + attributes = ExternalCredential.request_account_to_link(provider, callback_url(provider)) + + session[:request_token] = attributes[:request_token] + + redirect_to attributes[:authorize_url] + end + + def callback + return if deny_if_not_role(Z_ROLENAME_ADMIN) + provider = params[:provider].downcase + + channel = ExternalCredential.link_account(provider, session[:request_token], params) + + session[:request_token] = nil + + render json: channel + end + + private + + def callback_url(provider) + "#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/external_credentials/#{provider}/callback" + end + +end diff --git a/app/controllers/external_credentials_twitter_controller.rb b/app/controllers/external_credentials_twitter_controller.rb deleted file mode 100644 index 438ea55de..000000000 --- a/app/controllers/external_credentials_twitter_controller.rb +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ - -require 'twitter_oauth' - -class ExternalCredentialsTwitterController < ApplicationController - before_action :authentication_check - - def index - return if deny_if_not_role(Z_ROLENAME_ADMIN) - - twitter_credential = ExternalCredential.find_by(name: 'Twitter') - - # TODO: refactor - result = { - # consumer_key: nil, - # consumer_secret: nil, - } - if !twitter_credential.nil? - - # p twitter_credential.credentials.inspect - - result[:consumer_key] = twitter_credential.credentials[:consumer_key] - result[:consumer_secret] = twitter_credential.credentials[:consumer_secret] - result[:authorize_url] = twitter_credential.credentials[:authorize_url] - end - - render json: result, status: :ok - end - - def show - return if deny_if_not_role(Z_ROLENAME_ADMIN) - model_show_render(ExternalCredential, params) - end - - def create - return if deny_if_not_role(Z_ROLENAME_ADMIN) - - credentials = handle_credentials(params) - - # create object - twitter_credential = ExternalCredential.new( name: 'Twitter', credentials: credentials ) - - # save object - twitter_credential.save! - - redirect_to credentials[:authorize_url] - end - - def update - return if deny_if_not_role(Z_ROLENAME_ADMIN) - - credentials = handle_credentials(params) - - # find object - twitter_credential = ExternalCredential.find(params[:id]) - - # update object - twitter_credential.update_attributes!( name: 'Twitter', credentials: credentials ) - - redirect_to credentials.authorize_url - end - - def destroy - return if deny_if_not_role(Z_ROLENAME_ADMIN) - model_destory_render(ExternalCredential, params) - end - - def auth - # https://zammad.tld/twitter_auth?oauth_token=uP15WgAAAAAAivjgAAABUSUkP5Y&oauth_verifier=OlKro1xj7gBQ5cwdvlcYQEniiEm1THsd - params[:oauth_token] - params[:oauth_verifier] - - params.require(:name, :oauth_token, :oauth_verifier) - params.permit(:name, :oauth_token, :oauth_verifier) - - twitter_credential = ExternalCredential.find_by( name: 'Twitter' ) - - if ( twitter_credential[:credentials][:oauth_token] != params[:oauth_token] ) - # TODO: ERROR - end - - access_token = client.authorize( - twitter_credential[:credentials][:oauth_token], - twitter_credential[:credentials][:oauth_token_secret], - oauth_verifier: params[:oauth_verifier] - ) - - credentials = { - consumer_key: twitter_credential[:credentials][:consumer_key], - consumer_secret: twitter_credential[:credentials][:consumer_secret], - access_token: access_token.token, - access_token_secret: access_token.secret, - } - - twitter_credential.update_attributes!(credentials: credentials ) - - # TODO - redirect_to "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#admin/path/external_credentials_twitter" - end - - private - - def handle_credentials(params) - - params.require(:consumer_key) - params.require(:consumer_secret) - - params.permit(:consumer_key, :consumer_secret) - - credentials = { - consumer_key: params[:consumer_key], - consumer_secret: params[:consumer_secret], - } - - client = TwitterOAuth::Client.new( - consumer_key: credentials[:consumer_key], - consumer_secret: credentials[:consumer_secret], - ) - - # TODO: improve callback URL - request_token = client.request_token(oauth_callback: "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#{Rails.configuration.api_path}/external_credentials_twitter/Twitter/auth") - - credentials[:oauth_token] = request_token.token - credentials[:oauth_token_secret] = request_token.secret - credentials[:authorize_url] = request_token.authorize_url - - credentials - end -end diff --git a/app/models/external_credential.rb b/app/models/external_credential.rb index 45c1e33ef..830316238 100644 --- a/app/models/external_credential.rb +++ b/app/models/external_credential.rb @@ -1,4 +1,28 @@ -class ExternalCredential < ActiveRecord::Base +class ExternalCredential < ApplicationModel + include ApplicationLib + validates :name, presence: true store :credentials + + def self.app_verify(params) + backend = load_backend(params[:provider]) + backend.app_verify(params) + end + + def self.request_account_to_link(provider, callback) + backend = load_backend(provider) + backend.request_account_to_link(callback) + end + + def self.link_account(provider, request_token, params) + backend = load_backend(provider) + backend.link_account(request_token, params) + end + + def self.load_backend(provider) + adapter = "ExternalCredential::#{provider.camelcase}" + require "#{adapter.to_filename}" + load_adapter(adapter) + end + end diff --git a/config/routes/external_credentials.rb b/config/routes/external_credentials.rb new file mode 100644 index 000000000..a7801d693 --- /dev/null +++ b/config/routes/external_credentials.rb @@ -0,0 +1,15 @@ +Zammad::Application.routes.draw do + api_path = Rails.configuration.api_path + + # CRUD + match api_path + '/external_credentials', to: 'external_credentials#index', via: :get + match api_path + '/external_credentials/:id', to: 'external_credentials#show', via: :get + match api_path + '/external_credentials', to: 'external_credentials#create', via: :post + match api_path + '/external_credentials/:id', to: 'external_credentials#update', via: :put + match api_path + '/external_credentials/:id', to: 'external_credentials#destroy', via: :delete + + # callback URL + match api_path + '/external_credentials/:provider/link_account', to: 'external_credentials#link_account', via: :get + match api_path + '/external_credentials/:provider/callback', to: 'external_credentials#callback', via: :get + +end diff --git a/config/routes/external_credentials_twitter.rb b/config/routes/external_credentials_twitter.rb deleted file mode 100644 index 95ff62a78..000000000 --- a/config/routes/external_credentials_twitter.rb +++ /dev/null @@ -1,14 +0,0 @@ -Zammad::Application.routes.draw do - api_path = Rails.configuration.api_path - - # CRUD - match api_path + '/external_credentials_twitter', to: 'external_credentials_twitter#index', via: :get - match api_path + '/external_credentials_twitter/:id', to: 'external_credentials_twitter#show', via: :get - match api_path + '/external_credentials_twitter', to: 'external_credentials_twitter#create', via: :post - match api_path + '/external_credentials_twitter/:id', to: 'external_credentials_twitter#update', via: :put - match api_path + '/external_credentials_twitter/:id', to: 'external_credentials_twitter#destroy', via: :delete - - # callback URL - match api_path + '/external_credentials_twitter/:name/auth', to: 'external_credentials_twitter#auth', via: :get - -end diff --git a/lib/auto_wizard.rb b/lib/auto_wizard.rb index 0fd187351..42dd0d5bc 100644 --- a/lib/auto_wizard.rb +++ b/lib/auto_wizard.rb @@ -128,6 +128,7 @@ returns 'Channels' => 'Channel', 'EmailAddresses' => 'EmailAddress', 'Signatures' => 'Signature', + 'Groups' => 'Group', } model_map.each {|map_name, model| next if !auto_wizard_hash[map_name] diff --git a/lib/external_credential/twitter.rb b/lib/external_credential/twitter.rb new file mode 100644 index 000000000..3f42ce1a1 --- /dev/null +++ b/lib/external_credential/twitter.rb @@ -0,0 +1,66 @@ +class ExternalCredential::Twitter + + def self.app_verify(params) + attributes = { + consumer_key: params[:consumer_key], + consumer_secret: params[:consumer_secret], + } + request_account_to_link('', attributes) + attributes + end + + def self.request_account_to_link(callback_url, credentials = {}) + external_credential = ExternalCredential.find_by(name: 'twitter') + consumer = OAuth::Consumer.new( + credentials[:consumer_key] || external_credential.credentials[:consumer_key], + credentials[:consumer_secret] || external_credential.credentials[:consumer_secret], { + site: 'https://api.twitter.com' + }) + request_token = consumer.get_request_token(oauth_callback: callback_url) + { + request_token: request_token, + authorize_url: request_token.authorize_url, + } + end + + def self.link_account(request_token, params) + fail if request_token.params[:oauth_token] != params[:oauth_token] + + external_credential = ExternalCredential.find_by(name: 'twitter') + access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier]) + client = Twitter::REST::Client.new( + consumer_key: external_credential.credentials[:consumer_key], + consumer_secret: external_credential.credentials[:consumer_secret], + access_token: access_token.token, + access_token_secret: access_token.secret, + ) + user = client.user + + # create channel + Channel.create( + area: 'Twitter::Account', + options: { + adapter: 'twitter', + user: { + id: user.id, + screen_name: user.screen_name, + }, + auth: { + external_credential_id: external_credential.id, + oauth_token: access_token.token, + oauth_token_secret: access_token.secret, + }, + sync: { + search: [], + mentions: {}, + direct_messages: {} + } + }, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + + end + +end