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'}
|