Feature #24808 » 0006-oauth-Use-Redmine-s-permissions-as-OAuth2-scopes.patch
app/controllers/application_controller.rb | ||
---|---|---|
127 | 127 |
# Oauth |
128 | 128 |
if access_token.accessible? |
129 | 129 |
user = User.active.find_by_id(access_token.resource_owner_id) |
130 |
user.oauth_scope = access_token.scopes.all.map(&:to_sym) |
|
130 | 131 |
else |
131 | 132 |
doorkeeper_render_error |
132 | 133 |
end |
app/controllers/oauth2_applications_controller.rb | ||
---|---|---|
1 |
class Oauth2ApplicationsController < Doorkeeper::ApplicationsController |
|
2 | ||
3 |
private |
|
4 | ||
5 |
def application_params |
|
6 |
params[:doorkeeper_application] ||= {} |
|
7 |
params[:doorkeeper_application][:scopes] ||= [] |
|
8 | ||
9 |
scopes = Redmine::AccessControl.public_permissions.map{|p| p.name.to_s} |
|
10 | ||
11 |
if params[:doorkeeper_application][:scopes].is_a?(Array) |
|
12 |
scopes |= params[:doorkeeper_application][:scopes] |
|
13 |
else |
|
14 |
scopes |= params[:doorkeeper_application][:scopes].split(/\s+/) |
|
15 |
end |
|
16 |
params[:doorkeeper_application][:scopes] = scopes.join(' ') |
|
17 |
super |
|
18 |
end |
|
19 |
end |
app/models/user.rb | ||
---|---|---|
100 | 100 |
attr_accessor :password, :password_confirmation, :generate_password |
101 | 101 |
attr_accessor :last_before_login_on |
102 | 102 |
attr_accessor :remote_ip |
103 |
attr_writer :oauth_scope |
|
103 | 104 | |
104 | 105 |
LOGIN_LENGTH_LIMIT = 60 |
105 | 106 |
MAIL_LENGTH_LIMIT = 60 |
... | ... | |
704 | 705 |
def allowed_to?(action, context, options={}, &block) |
705 | 706 |
if context && context.is_a?(Project) |
706 | 707 |
return false unless context.allows_to?(action) |
707 |
# Admin users are authorized for anything else |
|
708 |
return true if admin? |
|
708 | ||
709 |
# Admin users are authorized for anything or what their oauth scope prescribes |
|
710 |
if admin? |
|
711 |
if @oauth_scope |
|
712 |
return Role.new(permissions: @oauth_scope).allowed_to?(action, @oauth_scope) |
|
713 |
else |
|
714 |
return true |
|
715 |
end |
|
716 |
end |
|
709 | 717 | |
710 | 718 |
roles = roles_for_project(context) |
711 | 719 |
return false unless roles |
712 | 720 | |
713 | 721 |
roles.any? {|role| |
714 | 722 |
(context.is_public? || role.member?) && |
715 |
role.allowed_to?(action) && |
|
723 |
role.allowed_to?(action, @oauth_scope) &&
|
|
716 | 724 |
(block_given? ? yield(role, self) : true) |
717 | 725 |
} |
718 | 726 |
elsif context && context.is_a?(Array) |
... | ... | |
725 | 733 |
elsif context |
726 | 734 |
raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil") |
727 | 735 |
elsif options[:global] |
728 |
# Admin users are always authorized |
|
729 |
return true if admin? |
|
736 |
# Admin users are always authorized, only limited by their oauth scope |
|
737 |
if admin? |
|
738 |
if @oauth_scope |
|
739 |
return Role.new(permissions: @oauth_scope).allowed_to?(action, @oauth_scope) |
|
740 |
else |
|
741 |
return true |
|
742 |
end |
|
743 |
end |
|
730 | 744 | |
731 | 745 |
# authorize if user has at least one role that has this permission |
732 | 746 |
roles = self.roles.to_a | [builtin_role] |
733 | 747 |
roles.any? {|role| |
734 |
role.allowed_to?(action) && |
|
748 |
role.allowed_to?(action, @oauth_scope) &&
|
|
735 | 749 |
(block_given? ? yield(role, self) : true) |
736 | 750 |
} |
737 | 751 |
else |
app/views/doorkeeper/applications/_form.html.erb | ||
---|---|---|
12 | 12 |
<% end %> |
13 | 13 |
</em> |
14 | 14 |
</p> |
15 |
</div> |
|
15 | 16 | |
16 |
<p> |
|
17 |
<%= f.text_field :scopes, :size => 60, :label => :'activerecord.attributes.doorkeeper/application.scopes' %> |
|
18 |
<em class="info"> |
|
19 |
<%= t('doorkeeper.applications.help.scopes') %> |
|
20 |
</em> |
|
21 |
</p> |
|
22 | ||
23 | ||
24 | ||
17 |
<h3><%= l(:'activerecord.attributes.doorkeeper/application.scopes') %></h3> |
|
18 |
<div class="box tabular" id="scopes"> |
|
19 |
<% perms_by_module = Redmine::AccessControl.permissions.group_by {|p| p.project_module.to_s} %> |
|
20 |
<% perms_by_module.keys.sort.each do |mod| %> |
|
21 |
<fieldset><legend><%= mod.blank? ? l(:label_project) : l_or_humanize(mod, :prefix => 'project_module_') %></legend> |
|
22 |
<% perms_by_module[mod].each do |permission| %> |
|
23 |
<label class="floating"> |
|
24 |
<%= check_box_tag 'doorkeeper_application[scopes][]', permission.name.to_s, (permission.public? || @application.scopes.include?( permission.name.to_s)), |
|
25 |
:id => "doorkeeper_application_scopes_#{permission.name}", |
|
26 |
:data => {:shows => ".#{permission.name}_shown"}, |
|
27 |
:disabled => permission.public? %> |
|
28 |
<%= l_or_humanize(permission.name, :prefix => 'permission_') %> |
|
29 |
</label> |
|
30 |
<% end %> |
|
31 |
</fieldset> |
|
32 |
<% end %> |
|
33 |
<br /><%= check_all_links 'scopes' %> |
|
34 |
<%= hidden_field_tag 'doorkeeper_application[scopes][]', '' %> |
|
25 | 35 |
</div> |
app/views/doorkeeper/applications/index.html.erb | ||
---|---|---|
18 | 18 |
<tr id="application_<%= application.id %>" class="<%= cycle("odd", "even") %>"> |
19 | 19 |
<td class="name"><span><%= link_to application.name, oauth_application_path(application) %></span></td> |
20 | 20 |
<td class="description"><%= truncate application.redirect_uri.split.join(', '), length: 50 %></td> |
21 |
<td class="description"><%= h application.scopes %></td>
|
|
21 |
<td class="description"><%= safe_join application.scopes.map{|scope| h l_or_humanize(scope, prefix: 'permission_')}, ", " %></td>
|
|
22 | 22 |
<td class="buttons"> |
23 | 23 |
<%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(application), class: 'icon icon-edit' %> |
24 | 24 |
<%= link_to t('doorkeeper.applications.buttons.destroy'), oauth_application_path(application), :data => {:confirm => t('doorkeeper.applications.confirmations.destroy')}, :method => :delete, :class => 'icon icon-del' %> |
app/views/doorkeeper/applications/show.html.erb | ||
---|---|---|
17 | 17 |
</p> |
18 | 18 |
<p> |
19 | 19 |
<span class="label"><%= t('.scopes') %>:</span> |
20 |
<code><%= h @application.scopes %></code>
|
|
20 |
<code><%= safe_join @application.scopes.map{|scope| h l_or_humanize(scope, prefix: 'permission_')}, ", " %></code>
|
|
21 | 21 |
</p> |
22 | 22 |
</div> |
23 | 23 |
app/views/doorkeeper/authorizations/new.html.erb | ||
---|---|---|
10 | 10 |
<p><%= t('.able_to') %>: |
11 | 11 |
<ul> |
12 | 12 |
<% @pre_auth.scopes.each do |scope| %> |
13 |
<li><%= t scope, scope: [:doorkeeper, :scopes] %></li>
|
|
13 |
<li><%= l_or_humanize(scope, prefix: 'permission_') %></li>
|
|
14 | 14 |
<% end %> |
15 | 15 |
</ul> |
16 | 16 |
</p> |
config/initializers/doorkeeper.rb | ||
---|---|---|
3 | 3 |
reuse_access_token |
4 | 4 |
realm Redmine::Info.app_name |
5 | 5 |
base_controller 'ApplicationController' |
6 |
default_scopes :public |
|
6 |
default_scopes *Redmine::AccessControl.public_permissions.map(&:name) |
|
7 |
optional_scopes *Redmine::AccessControl.permissions.map(&:name) |
|
7 | 8 | |
8 | 9 |
resource_owner_authenticator do |
9 | 10 |
if require_login |
config/routes.rb | ||
---|---|---|
19 | 19 | |
20 | 20 |
Rails.application.routes.draw do |
21 | 21 | |
22 |
use_doorkeeper |
|
22 |
use_doorkeeper do |
|
23 |
controllers :applications => 'oauth2_applications' |
|
24 |
end |
|
23 | 25 | |
24 | 26 |
root :to => 'welcome#index' |
25 | 27 |
root :to => 'welcome#index', :as => 'home' |
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'},
|
|
270 |
menu.push :applications, {:controller => 'oauth2_applications', :action => 'index'},
|
|
271 | 271 |
:if => Proc.new { Setting.rest_api_enabled? }, |
272 | 272 |
:caption => :'doorkeeper.layouts.admin.nav.applications', |
273 | 273 |
:html => {:class => 'icon icon-applications'} |