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 |