Feature #24808 » 0003-Add-OAuth2-provider-capability-using-doorkeeper-gem.patch
| Gemfile | ||
|---|---|---|
| 15 | 15 |
gem "actionpack-xml_parser" |
| 16 | 16 |
gem "roadie-rails" |
| 17 | 17 |
gem "mimemagic" |
| 18 |
gem "doorkeeper", "~>4.2.0" |
|
| 19 |
gem "doorkeeper-i18n", "~>4.0.0" |
|
| 18 | 20 | |
| 19 | 21 |
gem "nokogiri", "~> 1.6.8" |
| 20 | 22 | |
| app/controllers/application_controller.rb | ||
|---|---|---|
| 112 | 112 |
if (key = api_key_from_request) |
| 113 | 113 |
# Use API key |
| 114 | 114 |
user = User.find_by_api_key(key) |
| 115 |
elsif access_token = Doorkeeper.authenticate(request) |
|
| 116 |
if access_token.accessible? |
|
| 117 |
user = User.active.find_by_id(access_token.resource_owner_id) |
|
| 118 |
else |
|
| 119 |
doorkeeper_render_error |
|
| 120 |
end |
|
| 115 | 121 |
elsif request.authorization.to_s =~ /\ABasic /i |
| 116 | 122 |
# HTTP Basic, either username/password or API key/random |
| 117 | 123 |
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 | ||
|---|---|---|
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | 17 | |
| 18 | 18 |
Rails.application.routes.draw do |
| 19 | ||
| 20 |
use_doorkeeper |
|
| 21 | ||
| 22 |
root :to => 'welcome#index' |
|
| 19 | 23 |
root :to => 'welcome#index', :as => 'home' |
| 20 | 24 | |
| 21 | 25 |
match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post] |
| db/migrate/20170107092155_create_doorkeeper_tables.rb | ||
|---|---|---|
| 1 |
class CreateDoorkeeperTables < ActiveRecord::Migration |
|
| 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.timestamps null: false |
|
| 10 |
end |
|
| 11 | ||
| 12 |
add_index :oauth_applications, :uid, unique: true |
|
| 13 | ||
| 14 |
create_table :oauth_access_grants do |t| |
|
| 15 |
t.integer :resource_owner_id, null: false |
|
| 16 |
t.references :application, null: false |
|
| 17 |
t.string :token, null: false |
|
| 18 |
t.integer :expires_in, null: false |
|
| 19 |
t.text :redirect_uri, null: false |
|
| 20 |
t.datetime :created_at, null: false |
|
| 21 |
t.datetime :revoked_at |
|
| 22 |
t.text :scopes |
|
| 23 |
end |
|
| 24 | ||
| 25 |
add_index :oauth_access_grants, :token, unique: true |
|
| 26 |
add_foreign_key( |
|
| 27 |
:oauth_access_grants, |
|
| 28 |
:oauth_applications, |
|
| 29 |
column: :application_id |
|
| 30 |
) |
|
| 31 |
add_foreign_key( |
|
| 32 |
:oauth_access_grants, |
|
| 33 |
:users, |
|
| 34 |
column: :resource_owner_id |
|
| 35 |
) |
|
| 36 | ||
| 37 |
create_table :oauth_access_tokens do |t| |
|
| 38 |
t.integer :resource_owner_id |
|
| 39 |
t.references :application |
|
| 40 | ||
| 41 |
t.string :token, null: false |
|
| 42 | ||
| 43 |
t.string :refresh_token |
|
| 44 |
t.integer :expires_in |
|
| 45 |
t.datetime :revoked_at |
|
| 46 |
t.datetime :created_at, null: false |
|
| 47 |
t.text :scopes |
|
| 48 | ||
| 49 |
t.string :previous_refresh_token, null: false, default: "" |
|
| 50 |
end |
|
| 51 | ||
| 52 |
add_index :oauth_access_tokens, :token, unique: true |
|
| 53 |
add_index :oauth_access_tokens, :resource_owner_id |
|
| 54 |
add_index :oauth_access_tokens, :refresh_token, unique: true |
|
| 55 | ||
| 56 |
add_foreign_key( |
|
| 57 |
:oauth_access_tokens, |
|
| 58 |
:oauth_applications, |
|
| 59 |
column: :application_id |
|
| 60 |
) |
|
| 61 |
add_foreign_key( |
|
| 62 |
:oauth_access_tokens, |
|
| 63 |
:users, |
|
| 64 |
column: :resource_owner_id |
|
| 65 |
) |
|
| 66 |
end |
|
| 67 |
end |
|
| lib/redmine.rb | ||
|---|---|---|
| 245 | 245 |
:html => {:class => 'icon icon-server-authentication'}
|
| 246 | 246 |
menu.push :plugins, {:controller => 'admin', :action => 'plugins'}, :last => true,
|
| 247 | 247 |
:html => {:class => 'icon icon-plugins'}
|
| 248 |
menu.push :applications, {:controller => 'doorkeeper/applications', :action => 'index'}, :last => true,
|
|
| 249 |
:if => Proc.new { Setting.rest_api_enabled? },
|
|
| 250 |
:caption => :'doorkeeper.layouts.admin.nav.applications', |
|
| 251 |
:html => {:class => 'icon icon-applications'}
|
|
| 248 | 252 |
menu.push :info, {:controller => 'admin', :action => 'info'}, :caption => :label_information_plural, :last => true,
|
| 249 | 253 |
:html => {:class => 'icon icon-help'}
|
| 250 | 254 |
end |
| public/stylesheets/application.css | ||
|---|---|---|
| 1264 | 1264 |
.icon-workflows { background-image: url(../images/ticket_go.png); }
|
| 1265 | 1265 |
.icon-custom-fields { background-image: url(../images/textfield.png); }
|
| 1266 | 1266 |
.icon-plugins { background-image: url(../images/plugin.png); }
|
| 1267 |
.icon-applications { background-image: url(../images/application_view_tile.png); }
|
|
| 1267 | 1268 |
.icon-news { background-image: url(../images/news.png); }
|
| 1268 | 1269 |
.icon-issue-closed { background-image: url(../images/ticket_checked.png); }
|
| 1269 | 1270 |
.icon-issue-note { background-image: url(../images/ticket_note.png); }
|
| test/unit/lib/redmine/i18n_test.rb | ||
|---|---|---|
| 184 | 184 |
def test_languages_options |
| 185 | 185 |
options = languages_options |
| 186 | 186 |
assert options.is_a?(Array) |
| 187 |
assert_equal valid_languages.size, options.size |
|
| 187 |
assert_equal valid_languages.select {|locale| ::I18n.exists?(:general_lang_name, locale)}.size, options.size
|
|
| 188 | 188 |
assert_nil options.detect {|option| !option.is_a?(Array)}
|
| 189 | 189 |
assert_nil options.detect {|option| option.size != 2}
|
| 190 | 190 |
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 |