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 |