Feature #24808 » 0003-oauth-Add-OAuth2-provider-capability-using-doorkeepe.patch
| Gemfile | ||
|---|---|---|
| 18 | 18 |
gem "rbpdf", "~> 1.20.0" |
| 19 | 19 |
gem 'addressable' |
| 20 | 20 |
gem 'rubyzip', (RUBY_VERSION < '2.4' ? '~> 1.3.0' : '~> 2.3.0') |
| 21 |
gem "doorkeeper", "~> 4.4.0" |
|
| 22 |
gem "doorkeeper-i18n", "~> 4.0" |
|
| 21 | 23 | |
| 22 | 24 |
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem |
| 23 | 25 |
gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin] |
| app/controllers/application_controller.rb | ||
|---|---|---|
| 123 | 123 |
if (key = api_key_from_request) |
| 124 | 124 |
# Use API key |
| 125 | 125 |
user = User.find_by_api_key(key) |
| 126 |
elsif access_token = Doorkeeper.authenticate(request) |
|
| 127 |
# Oauth |
|
| 128 |
if access_token.accessible? |
|
| 129 |
user = User.active.find_by_id(access_token.resource_owner_id) |
|
| 130 |
else |
|
| 131 |
doorkeeper_render_error |
|
| 132 |
end |
|
| 126 | 133 |
elsif /\ABasic /i.match?(request.authorization.to_s) |
| 127 | 134 |
# HTTP Basic, either username/password or API key/random |
| 128 | 135 |
authenticate_with_http_basic do |username, password| |
| app/views/my/account.html.erb | ||
|---|---|---|
| 1 | 1 |
<div class="contextual"> |
| 2 | 2 |
<%= additional_emails_link(@user) %> |
| 3 | 3 |
<%= link_to(l(:button_change_password), {:action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %>
|
| 4 |
<%= link_to(t('doorkeeper.applications.index.title'), oauth_authorized_applications_path, :class => 'icon icon-applications') if Setting.rest_api_enabled? %>
|
|
| 4 | 5 |
<%= call_hook(:view_my_account_contextual, :user => @user)%> |
| 5 | 6 |
</div> |
| 6 | 7 | |
| config/initializers/doorkeeper.rb | ||
|---|---|---|
| 1 |
Doorkeeper.configure do |
|
| 2 |
use_refresh_token |
|
| 3 |
reuse_access_token |
|
| 4 |
realm Redmine::Info.app_name |
|
| 5 |
default_scopes :public |
|
| 6 | ||
| 7 |
resource_owner_authenticator do |
|
| 8 |
if Setting.rest_api_enabled? |
|
| 9 |
User.active.find_by_id(session[:user_id]) || redirect_to(signin_path(:back_url => request.original_url)) |
|
| 10 |
else |
|
| 11 |
render(:text => 'Forbidden', :status => 403) |
|
| 12 |
end |
|
| 13 |
end |
|
| 14 | ||
| 15 |
admin_authenticator do |
|
| 16 |
if !Setting.rest_api_enabled? || !User.active.where(admin: true).find_by_id(session[:user_id]) |
|
| 17 |
render(:text => 'Forbidden', :status => 403) |
|
| 18 |
end |
|
| 19 |
end |
|
| 20 | ||
| 21 |
end |
|
| config/routes.rb | ||
|---|---|---|
| 18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | 19 | |
| 20 | 20 |
Rails.application.routes.draw do |
| 21 | ||
| 22 |
use_doorkeeper |
|
| 23 | ||
| 24 |
root :to => 'welcome#index' |
|
| 21 | 25 |
root :to => 'welcome#index', :as => 'home' |
| 22 | 26 | |
| 23 | 27 |
match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post] |
| db/migrate/20170107092155_create_doorkeeper_tables.rb | ||
|---|---|---|
| 1 |
class CreateDoorkeeperTables < ActiveRecord::Migration[4.2] |
|
| 2 |
def change |
|
| 3 |
create_table :oauth_applications do |t| |
|
| 4 |
t.string :name, null: false |
|
| 5 |
t.string :uid, null: false |
|
| 6 |
t.string :secret, null: false |
|
| 7 |
t.text :redirect_uri, null: false |
|
| 8 |
t.text :scopes, null: false |
|
| 9 |
t.boolean :confidential, null: false, default: true |
|
| 10 |
t.timestamps null: false |
|
| 11 |
end |
|
| 12 | ||
| 13 |
add_index :oauth_applications, :uid, unique: true |
|
| 14 | ||
| 15 |
create_table :oauth_access_grants do |t| |
|
| 16 |
t.integer :resource_owner_id, null: false |
|
| 17 |
t.references :application, null: false |
|
| 18 |
t.string :token, null: false |
|
| 19 |
t.integer :expires_in, null: false |
|
| 20 |
t.text :redirect_uri, null: false |
|
| 21 |
t.datetime :created_at, null: false |
|
| 22 |
t.datetime :revoked_at |
|
| 23 |
t.text :scopes |
|
| 24 |
end |
|
| 25 | ||
| 26 |
add_index :oauth_access_grants, :token, unique: true |
|
| 27 |
add_foreign_key( |
|
| 28 |
:oauth_access_grants, |
|
| 29 |
:oauth_applications, |
|
| 30 |
column: :application_id |
|
| 31 |
) |
|
| 32 |
add_foreign_key( |
|
| 33 |
:oauth_access_grants, |
|
| 34 |
:users, |
|
| 35 |
column: :resource_owner_id |
|
| 36 |
) |
|
| 37 | ||
| 38 |
create_table :oauth_access_tokens do |t| |
|
| 39 |
t.integer :resource_owner_id |
|
| 40 |
t.references :application |
|
| 41 | ||
| 42 |
t.string :token, null: false |
|
| 43 | ||
| 44 |
t.string :refresh_token |
|
| 45 |
t.integer :expires_in |
|
| 46 |
t.datetime :revoked_at |
|
| 47 |
t.datetime :created_at, null: false |
|
| 48 |
t.text :scopes |
|
| 49 | ||
| 50 |
t.string :previous_refresh_token, null: false, default: "" |
|
| 51 |
end |
|
| 52 | ||
| 53 |
add_index :oauth_access_tokens, :token, unique: true |
|
| 54 |
add_index :oauth_access_tokens, :resource_owner_id |
|
| 55 |
add_index :oauth_access_tokens, :refresh_token, unique: true |
|
| 56 | ||
| 57 |
add_foreign_key( |
|
| 58 |
:oauth_access_tokens, |
|
| 59 |
:oauth_applications, |
|
| 60 |
column: :application_id |
|
| 61 |
) |
|
| 62 |
add_foreign_key( |
|
| 63 |
:oauth_access_tokens, |
|
| 64 |
:users, |
|
| 65 |
column: :resource_owner_id |
|
| 66 |
) |
|
| 67 |
end |
|
| 68 |
end |
|
| lib/redmine.rb | ||
|---|---|---|
| 267 | 267 |
:html => {:class => 'icon icon-settings'}
|
| 268 | 268 |
menu.push :ldap_authentication, {:controller => 'auth_sources', :action => 'index'},
|
| 269 | 269 |
:html => {:class => 'icon icon-server-authentication'}
|
| 270 |
menu.push :applications, {:controller => 'doorkeeper/applications', :action => 'index'},
|
|
| 271 |
:if => Proc.new { Setting.rest_api_enabled? },
|
|
| 272 |
:caption => :'doorkeeper.layouts.admin.nav.applications', |
|
| 273 |
:html => {:class => 'icon icon-applications'}
|
|
| 270 | 274 |
menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true,
|
| 271 | 275 |
:html => {:class => 'icon icon-plugins'}
|
| 272 | 276 |
menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true,
|
| public/stylesheets/application.css | ||
|---|---|---|
| 1544 | 1544 |
.icon-workflows { background-image: url(../images/ticket_go.png); }
|
| 1545 | 1545 |
.icon-custom-fields { background-image: url(../images/textfield.png); }
|
| 1546 | 1546 |
.icon-plugins { background-image: url(../images/plugin.png); }
|
| 1547 |
.icon-applications { background-image: url(../images/application_view_tile.png); }
|
|
| 1547 | 1548 |
.icon-news { background-image: url(../images/news.png); }
|
| 1548 | 1549 |
.icon-issue-closed { background-image: url(../images/ticket_checked.png); }
|
| 1549 | 1550 |
.icon-issue-note { background-image: url(../images/ticket_note.png); }
|
| test/unit/lib/redmine/i18n_test.rb | ||
|---|---|---|
| 185 | 185 |
def test_languages_options |
| 186 | 186 |
options = languages_options |
| 187 | 187 |
assert options.is_a?(Array) |
| 188 |
assert_equal valid_languages.size, options.size |
|
| 188 |
assert_equal valid_languages.select {|locale| ::I18n.exists?(:general_lang_name, locale)}.size, options.size
|
|
| 189 | 189 |
assert_nil options.detect {|option| !option.is_a?(Array)}
|
| 190 | 190 |
assert_nil options.detect {|option| option.size != 2}
|
| 191 | 191 |
assert_nil options.detect {|option| !option.first.is_a?(String) || !option.last.is_a?(String)}
|
| ... | ... | |
| 205 | 205 | |
| 206 | 206 |
def test_locales_validness |
| 207 | 207 |
lang_files_count = Dir["#{Rails.root}/config/locales/*.yml"].size
|
| 208 |
assert_equal lang_files_count, valid_languages.size |
|
| 208 |
assert_equal lang_files_count, valid_languages.select {|locale| ::I18n.exists?(:general_lang_name, locale)}.size
|
|
| 209 | 209 |
valid_languages.each do |lang| |
| 210 | 210 |
assert set_language_if_valid(lang) |
| 211 | 211 |
end |