From 78d504623c685c21aaee646fda8a7df87d399ff1 Mon Sep 17 00:00:00 2001 From: Jan Schulz-Hofen Date: Tue, 10 Jan 2017 16:50:01 +0100 Subject: [PATCH 3/7] [oauth] Add OAuth2 provider capability using doorkeeper gem --- Gemfile | 2 + app/controllers/application_controller.rb | 7 ++ app/views/my/account.html.erb | 1 + config/initializers/doorkeeper.rb | 21 ++++++ config/routes.rb | 4 ++ ...20170107092155_create_doorkeeper_tables.rb | 68 ++++++++++++++++++ lib/redmine.rb | 4 ++ public/images/application_view_tile.png | Bin 0 -> 465 bytes public/stylesheets/application.css | 1 + test/unit/lib/redmine/i18n_test.rb | 4 +- 10 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 config/initializers/doorkeeper.rb create mode 100644 db/migrate/20170107092155_create_doorkeeper_tables.rb create mode 100755 public/images/application_view_tile.png diff --git a/Gemfile b/Gemfile index d11f085d0..26d633ab3 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,8 @@ gem 'i18n', '~> 1.8.2' gem "rbpdf", "~> 1.20.0" gem 'addressable' gem 'rubyzip', (RUBY_VERSION < '2.4' ? '~> 1.3.0' : '~> 2.3.0') +gem "doorkeeper", "~> 4.4.0" +gem "doorkeeper-i18n", "~> 4.0" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin] diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fca9ebc90..02f874142 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -123,6 +123,13 @@ class ApplicationController < ActionController::Base if (key = api_key_from_request) # Use API key user = User.find_by_api_key(key) + elsif access_token = Doorkeeper.authenticate(request) + # Oauth + if access_token.accessible? + user = User.active.find_by_id(access_token.resource_owner_id) + else + doorkeeper_render_error + end elsif /\ABasic /i.match?(request.authorization.to_s) # HTTP Basic, either username/password or API key/random authenticate_with_http_basic do |username, password| diff --git a/app/views/my/account.html.erb b/app/views/my/account.html.erb index 87b2d7cbd..32e6c9724 100644 --- a/app/views/my/account.html.erb +++ b/app/views/my/account.html.erb @@ -1,6 +1,7 @@
<%= additional_emails_link(@user) %> <%= link_to(l(:button_change_password), {:action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %> +<%= link_to(t('doorkeeper.applications.index.title'), oauth_authorized_applications_path, :class => 'icon icon-applications') if Setting.rest_api_enabled? %> <%= call_hook(:view_my_account_contextual, :user => @user)%>
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb new file mode 100644 index 000000000..b300cd59a --- /dev/null +++ b/config/initializers/doorkeeper.rb @@ -0,0 +1,21 @@ +Doorkeeper.configure do + use_refresh_token + reuse_access_token + realm Redmine::Info.app_name + default_scopes :public + + resource_owner_authenticator do + if Setting.rest_api_enabled? + User.active.find_by_id(session[:user_id]) || redirect_to(signin_path(:back_url => request.original_url)) + else + render(:text => 'Forbidden', :status => 403) + end + end + + admin_authenticator do + if !Setting.rest_api_enabled? || !User.active.where(admin: true).find_by_id(session[:user_id]) + render(:text => 'Forbidden', :status => 403) + end + end + +end diff --git a/config/routes.rb b/config/routes.rb index 6ff7bc3a1..4dc9da569 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Rails.application.routes.draw do + + use_doorkeeper + + root :to => 'welcome#index' root :to => 'welcome#index', :as => 'home' match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post] diff --git a/db/migrate/20170107092155_create_doorkeeper_tables.rb b/db/migrate/20170107092155_create_doorkeeper_tables.rb new file mode 100644 index 000000000..b5585c105 --- /dev/null +++ b/db/migrate/20170107092155_create_doorkeeper_tables.rb @@ -0,0 +1,68 @@ +class CreateDoorkeeperTables < ActiveRecord::Migration[4.2] + def change + create_table :oauth_applications do |t| + t.string :name, null: false + t.string :uid, null: false + t.string :secret, null: false + t.text :redirect_uri, null: false + t.text :scopes, null: false + t.boolean :confidential, null: false, default: true + t.timestamps null: false + end + + add_index :oauth_applications, :uid, unique: true + + create_table :oauth_access_grants do |t| + t.integer :resource_owner_id, null: false + t.references :application, null: false + t.string :token, null: false + t.integer :expires_in, null: false + t.text :redirect_uri, null: false + t.datetime :created_at, null: false + t.datetime :revoked_at + t.text :scopes + end + + add_index :oauth_access_grants, :token, unique: true + add_foreign_key( + :oauth_access_grants, + :oauth_applications, + column: :application_id + ) + add_foreign_key( + :oauth_access_grants, + :users, + column: :resource_owner_id + ) + + create_table :oauth_access_tokens do |t| + t.integer :resource_owner_id + t.references :application + + t.string :token, null: false + + t.string :refresh_token + t.integer :expires_in + t.datetime :revoked_at + t.datetime :created_at, null: false + t.text :scopes + + t.string :previous_refresh_token, null: false, default: "" + end + + add_index :oauth_access_tokens, :token, unique: true + add_index :oauth_access_tokens, :resource_owner_id + add_index :oauth_access_tokens, :refresh_token, unique: true + + add_foreign_key( + :oauth_access_tokens, + :oauth_applications, + column: :application_id + ) + add_foreign_key( + :oauth_access_tokens, + :users, + column: :resource_owner_id + ) + end +end diff --git a/lib/redmine.rb b/lib/redmine.rb index 668a65da2..038c7cb4d 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -267,6 +267,10 @@ Redmine::MenuManager.map :admin_menu do |menu| :html => {:class => 'icon icon-settings'} menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'}, :html => {:class => 'icon icon-server-authentication'} + menu.push :applications, {:controller => 'doorkeeper/applications', :action => 'index'}, + :if => Proc.new { Setting.rest_api_enabled? }, + :caption => :'doorkeeper.layouts.admin.nav.applications', + :html => {:class => 'icon icon-applications'} menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true, :html => {:class => 'icon icon-plugins'} menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true, diff --git a/public/images/application_view_tile.png b/public/images/application_view_tile.png new file mode 100755 index 0000000000000000000000000000000000000000..3bc0bd32fceb21d70368f7842a00a53d6369ba48 GIT binary patch literal 465 zcmV;?0WSWDP)zJNu3H-P zO&@UpeyZQXi7jKe-Hk?r-sue;aDce_XqkvXP+W#F_*ot`jB?BS93Uw71|U^ZjLH`yP%FO7U<6!nLCG} z$SDlW