Feature #24808 » 0006-Use-Redmine-s-permissions-as-OAuth2-scopes.patch
| app/controllers/application_controller.rb | ||
|---|---|---|
| 115 | 115 |
elsif access_token = Doorkeeper.authenticate(request) |
| 116 | 116 |
if access_token.accessible? |
| 117 | 117 |
user = User.active.find_by_id(access_token.resource_owner_id) |
| 118 |
user.oauth_scope = access_token.scopes.all.map(&:to_sym) |
|
| 118 | 119 |
else |
| 119 | 120 |
doorkeeper_render_error |
| 120 | 121 |
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 | ||
|---|---|---|
| 98 | 98 |
attr_accessor :password, :password_confirmation, :generate_password |
| 99 | 99 |
attr_accessor :last_before_login_on |
| 100 | 100 |
attr_accessor :remote_ip |
| 101 |
attr_writer :oauth_scope |
|
| 101 | 102 | |
| 102 | 103 |
# Prevents unauthorized assignments |
| 103 | 104 |
attr_protected :password, :password_confirmation, :hashed_password |
| ... | ... | |
| 654 | 655 |
def allowed_to?(action, context, options={}, &block)
|
| 655 | 656 |
if context && context.is_a?(Project) |
| 656 | 657 |
return false unless context.allows_to?(action) |
| 657 |
# Admin users are authorized for anything else |
|
| 658 |
return true if admin? |
|
| 658 |
# Admin users are authorized for anything or what their oauth scope prescribes |
|
| 659 |
if admin? && @oauth_scope.present? |
|
| 660 |
Role.new(permissions: @oauth_scope).allowed_to?(action, @oauth_scope) |
|
| 661 |
elsif admin? |
|
| 662 |
return true |
|
| 663 |
end |
|
| 659 | 664 | |
| 660 | 665 |
roles = roles_for_project(context) |
| 661 | 666 |
return false unless roles |
| 662 | 667 |
roles.any? {|role|
|
| 663 | 668 |
(context.is_public? || role.member?) && |
| 664 |
role.allowed_to?(action) && |
|
| 669 |
role.allowed_to?(action, @oauth_scope) &&
|
|
| 665 | 670 |
(block_given? ? yield(role, self) : true) |
| 666 | 671 |
} |
| 667 | 672 |
elsif context && context.is_a?(Array) |
| ... | ... | |
| 674 | 679 |
elsif context |
| 675 | 680 |
raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
|
| 676 | 681 |
elsif options[:global] |
| 677 |
# Admin users are always authorized |
|
| 678 |
return true if admin? |
|
| 682 |
# Admin users are always authorized, only limited by their oauth scope |
|
| 683 |
if admin? && @oauth_scope.present? |
|
| 684 |
Role.new(permissions: @oauth_scope).allowed_to?(action, @oauth_scope) |
|
| 685 |
elsif admin? |
|
| 686 |
return true |
|
| 687 |
end |
|
| 679 | 688 | |
| 680 | 689 |
# authorize if user has at least one role that has this permission |
| 681 | 690 |
roles = self.roles.to_a | [builtin_role] |
| 682 | 691 |
roles.any? {|role|
|
| 683 |
role.allowed_to?(action) && |
|
| 692 |
role.allowed_to?(action, @oauth_scope) &&
|
|
| 684 | 693 |
(block_given? ? yield(role, self) : true) |
| 685 | 694 |
} |
| 686 | 695 |
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> |
| 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 | ||
|---|---|---|
| 17 | 17 | |
| 18 | 18 |
Rails.application.routes.draw do |
| 19 | 19 | |
| 20 |
use_doorkeeper |
|
| 20 |
use_doorkeeper do |
|
| 21 |
controllers :applications => 'oauth2_applications' |
|
| 22 |
end |
|
| 21 | 23 | |
| 22 | 24 |
root :to => 'welcome#index' |
| 23 | 25 |
root :to => 'welcome#index', :as => 'home' |
| 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,
|
|
| 248 |
menu.push :applications, {:controller => 'oauth2_applications', :action => 'index'}, :last => true,
|
|
| 249 | 249 |
:if => Proc.new { Setting.rest_api_enabled? },
|
| 250 | 250 |
:caption => :'doorkeeper.layouts.admin.nav.applications', |
| 251 | 251 |
:html => {:class => 'icon icon-applications'}
|