Skip to content

Commit

Permalink
Merge 5d939e6 into 26e7263
Browse files Browse the repository at this point in the history
  • Loading branch information
tomhughes committed Apr 13, 2021
2 parents 26e7263 + 5d939e6 commit 475ecac
Show file tree
Hide file tree
Showing 41 changed files with 1,814 additions and 36 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Rails/HttpPositionalArguments:
Rails/InverseOf:
Enabled: false

Rails/ReflectionClassName:
Enabled: false

Rails/SkipsModelValidations:
Exclude:
- 'db/migrate/*.rb'
Expand Down
4 changes: 4 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ Rails/HelperInstanceVariable:
Exclude:
- 'app/helpers/title_helper.rb'

Rails/LexicallyScopedActionFilter:
Exclude:
- 'app/controllers/oauth2_applications_controller.rb'

# Offense count: 5
# Configuration parameters: Include.
# Include: db/migrate/*.rb
Expand Down
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ gem "omniauth-openid"
gem "omniauth-rails_csrf_protection", "~> 1.0"
gem "omniauth-windowslive"

# Doorkeeper for OAuth2
gem "doorkeeper"
gem "doorkeeper-i18n"

# Markdown formatting support
gem "kramdown"

Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ GEM
activerecord (>= 3.0, < 6.2)
delayed_job (>= 3.0, < 5)
docile (1.3.5)
doorkeeper (5.5.1)
railties (>= 5)
doorkeeper-i18n (5.2.2)
doorkeeper (>= 5.2)
dry-configurable (0.12.1)
concurrent-ruby (~> 1.0)
dry-core (~> 0.5, >= 0.5.0)
Expand Down Expand Up @@ -491,6 +495,8 @@ DEPENDENCIES
dalli
debug_inspector (< 1.0.0)
delayed_job_active_record
doorkeeper
doorkeeper-i18n
dynamic_form
erb_lint
factory_bot_rails
Expand Down
3 changes: 3 additions & 0 deletions app/abilities/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def initialize(user)

if Settings.status != "database_offline"
can [:index, :new, :create, :show, :edit, :update, :destroy], ClientApplication
can [:index, :new, :create, :show, :edit, :update, :destroy], :oauth2_application
can [:index, :destroy], :oauth2_authorized_application
can [:new, :show, :create, :destroy], :oauth2_authorization
can [:new, :create, :edit, :update, :comment, :subscribe, :unsubscribe], DiaryEntry
can [:make_friend, :remove_friend], Friendship
can [:new, :create, :reply, :show, :inbox, :outbox, :mark, :destroy], Message
Expand Down
50 changes: 28 additions & 22 deletions app/abilities/api_capability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,43 @@ class ApiCapability

def initialize(token)
if Settings.status != "database_offline"
can [:create, :comment, :close, :reopen], Note if capability?(token, :allow_write_notes)
can [:show, :data], Trace if capability?(token, :allow_read_gpx)
can [:create, :update, :destroy], Trace if capability?(token, :allow_write_gpx)
can [:details], User if capability?(token, :allow_read_prefs)
can [:gpx_files], User if capability?(token, :allow_read_gpx)
can [:index, :show], UserPreference if capability?(token, :allow_read_prefs)
can [:update, :update_all, :destroy], UserPreference if capability?(token, :allow_write_prefs)
user = if token.respond_to?(:resource_owner_id)
User.find(token.resource_owner_id)
elsif token.respond_to?(:user)
token.user
end

if token&.user&.terms_agreed?
can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset if capability?(token, :allow_write_api)
can :create, ChangesetComment if capability?(token, :allow_write_api)
can [:create, :update, :delete], Node if capability?(token, :allow_write_api)
can [:create, :update, :delete], Way if capability?(token, :allow_write_api)
can [:create, :update, :delete], Relation if capability?(token, :allow_write_api)
can [:create, :comment, :close, :reopen], Note if scope?(token, :write_notes)
can [:show, :data], Trace if scope?(token, :read_gpx)
can [:create, :update, :destroy], Trace if scope?(token, :write_gpx)
can [:details], User if scope?(token, :read_prefs)
can [:gpx_files], User if scope?(token, :read_gpx)
can [:index, :show], UserPreference if scope?(token, :read_prefs)
can [:update, :update_all, :destroy], UserPreference if scope?(token, :write_prefs)

if user&.terms_agreed?
can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset if scope?(token, :write_api)
can :create, ChangesetComment if scope?(token, :write_api)
can [:create, :update, :delete], Node if scope?(token, :write_api)
can [:create, :update, :delete], Way if scope?(token, :write_api)
can [:create, :update, :delete], Relation if scope?(token, :write_api)
end

if token&.user&.moderator?
can [:destroy, :restore], ChangesetComment if capability?(token, :allow_write_api)
can :destroy, Note if capability?(token, :allow_write_notes)
if token&.user&.terms_agreed?
can :redact, OldNode if capability?(token, :allow_write_api)
can :redact, OldWay if capability?(token, :allow_write_api)
can :redact, OldRelation if capability?(token, :allow_write_api)
if user&.moderator?
can [:destroy, :restore], ChangesetComment if scope?(token, :write_api)
can :destroy, Note if scope?(token, :write_notes)
if user&.terms_agreed?
can :redact, OldNode if scope?(token, :write_api)
can :redact, OldWay if scope?(token, :write_api)
can :redact, OldRelation if scope?(token, :write_api)
end
end
end
end

private

def capability?(token, cap)
token&.read_attribute(cap)
def scope?(token, scope)
token&.includes_scope?(scope)
end
end
12 changes: 9 additions & 3 deletions app/controllers/api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@ def authorize(realm = "Web Password", errormessage = "Couldn't authenticate you"

def current_ability
# Use capabilities from the oauth token if it exists and is a valid access token
if Authenticator.new(self, [:token]).allow?
if doorkeeper_token&.accessible?
ApiAbility.new(nil).merge(ApiCapability.new(doorkeeper_token))
elsif Authenticator.new(self, [:token]).allow?
ApiAbility.new(nil).merge(ApiCapability.new(current_token))
else
ApiAbility.new(current_user)
end
end

def deny_access(_exception)
if current_token
if doorkeeper_token || current_token
set_locale
report_error t("oauth.permissions.missing"), :forbidden
elsif current_user
Expand All @@ -94,7 +96,11 @@ def gpx_status
# is optional.
def setup_user_auth
# try and setup using OAuth
unless Authenticator.new(self, [:token]).allow?
if doorkeeper_token&.accessible?
self.current_user = User.find(doorkeeper_token.resource_owner_id)
elsif Authenticator.new(self, [:token]).allow?
# self.current_user setup by OAuth
else
username, passwd = get_auth_data # parse from headers
# authenticate per-scheme
self.current_user = if username.nil?
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,13 @@ def current_ability
end

def deny_access(_exception)
if current_token
if doorkeeper_token || current_token
set_locale
report_error t("oauth.permissions.missing"), :forbidden
elsif current_user
set_locale
respond_to do |format|
format.html { redirect_to :controller => "errors", :action => "forbidden" }
format.html { redirect_to :controller => "/errors", :action => "forbidden" }
format.any { report_error t("application.permission_denied"), :forbidden }
end
elsif request.get?
Expand Down
28 changes: 28 additions & 0 deletions app/controllers/oauth2_applications_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Oauth2ApplicationsController < Doorkeeper::ApplicationsController
layout "site"

prepend_before_action :authorize_web
before_action :set_locale
before_action :set_application, :only => [:show, :edit, :update, :destroy]

authorize_resource :class => false

def index
@applications = current_resource_owner.oauth2_applications.ordered_by(:created_at)
end

private

def set_application
@application = current_resource_owner&.oauth2_applications&.find(params[:id])
rescue ActiveRecord::RecordNotFound
render :action => "not_found", :status => :not_found
end

def application_params
params[:doorkeeper_application][:scopes]&.delete("")
params.require(:doorkeeper_application)
.permit(:name, :redirect_uri, :confidential, :scopes => [])
.merge(:owner => current_resource_owner)
end
end
8 changes: 8 additions & 0 deletions app/controllers/oauth2_authorizations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Oauth2AuthorizationsController < Doorkeeper::AuthorizationsController
layout "site"

prepend_before_action :authorize_web
before_action :set_locale

authorize_resource :class => false
end
8 changes: 8 additions & 0 deletions app/controllers/oauth2_authorized_applications_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Oauth2AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
layout "site"

prepend_before_action :authorize_web
before_action :set_locale

authorize_resource :class => false
end
4 changes: 4 additions & 0 deletions app/models/access_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class AccessToken < OauthToken

before_create :set_authorized_at

def includes_scope?(scope)
self[:"allow_#{scope}"]
end

protected

def set_authorized_at
Expand Down
7 changes: 1 addition & 6 deletions app/models/client_application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def self.verify_request(request, options = {}, &block)
end

def self.all_permissions
PERMISSIONS
Oauth.scopes.collect { |s| :"allow_#{s.name}" }
end

def oauth_server
Expand Down Expand Up @@ -102,11 +102,6 @@ def permissions

protected

# this is the set of permissions that the client can ask for. clients
# have to say up-front what permissions they want and when users sign up they
# can agree or not agree to each of them.
PERMISSIONS = [:allow_read_prefs, :allow_write_prefs, :allow_write_diary, :allow_write_api, :allow_read_gpx, :allow_write_gpx, :allow_write_notes].freeze

def generate_keys
self.key = OAuth::Helper.generate_key(40)[0, 40]
self.secret = OAuth::Helper.generate_key(40)[0, 40]
Expand Down
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class User < ApplicationRecord
has_many :client_applications
has_many :oauth_tokens, -> { order(:authorized_at => :desc).preload(:client_application) }, :class_name => "OauthToken"

has_many :oauth2_applications, :class_name => Doorkeeper.config.application_model.name, :foreign_key => :owner_id

has_many :blocks, :class_name => "UserBlock"
has_many :blocks_created, :class_name => "UserBlock", :foreign_key => :creator_id
has_many :blocks_revoked, :class_name => "UserBlock", :foreign_key => :revoker_id
Expand Down
23 changes: 23 additions & 0 deletions app/views/oauth2_applications/_application.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<tr>
<td class="align-middle">
<ul class="list-unstyled mb-0">
<li><%= application.name %></li>
<% application.redirect_uri.split.each do |uri| -%>
<li class="text-muted"><%= uri %></li>
<% end -%>
</ul>
</td>
<td class="align-middle">
<ul class="list-unstyled mb-0">
<% application.scopes.each do |scope| -%>
<li><%= t "oauth.scopes.#{scope}" %></li>
<% end -%>
</ul>
</td>
<td class="align-middle">
<%= link_to t(".edit"), edit_oauth_application_path(application), :class => "btn btn-outline-primary" %>
</td>
<td class="align-middle">
<%= link_to t(".delete"), oauth_application_path(application), { :method => :delete, :class => "btn btn-outline-danger", :data => { :confirm => t(".confirm_delete") } } %>
</td>
</tr>
5 changes: 5 additions & 0 deletions app/views/oauth2_applications/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%= f.text_field :name %>
<%= f.text_area :redirect_uri, :help => t(".redirect_uri_help") %>
<%= f.check_box :confidential, :help => t(".confidential_help") %>
<%= f.collection_check_boxes :scopes, Oauth.scopes, :name, :description %>
<%= f.primary %>
7 changes: 7 additions & 0 deletions app/views/oauth2_applications/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<% content_for :heading do %>
<h1><%= t ".title" %></h1>
<% end %>
<%= bootstrap_form_for @application, :url => oauth_application_path(@application), :html => { :method => :put } do |f| %>
<%= render :partial => "form", :locals => { :f => f } %>
<% end %>
13 changes: 13 additions & 0 deletions app/views/oauth2_applications/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<% content_for :heading do %>
<h1><%= t ".title" %></h1>
<% end %>

<p>
<%= link_to t(".new"), new_oauth_application_path, :class => "btn btn-outline-primary" %>
</p>

<table class="table table-borderless table-striped">
<tbody>
<%= render :partial => "application", :collection => @applications %>
</tbody>
</table>
7 changes: 7 additions & 0 deletions app/views/oauth2_applications/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<% content_for :heading do %>
<h1><%= t ".title" %></h1>
<% end %>
<%= bootstrap_form_for @application, :url => { :action => :create } do |f| %>
<%= render :partial => "form", :locals => { :f => f } %>
<% end %>
1 change: 1 addition & 0 deletions app/views/oauth2_applications/not_found.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p><%= t ".sorry" %></p>
38 changes: 38 additions & 0 deletions app/views/oauth2_applications/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<% content_for :heading do %>
<h1><%= @application.name %></h1>
<% end %>
<% secret = flash[:application_secret].presence || @application.plaintext_secret %>

<table class="table table-borderless">
<tr>
<th><%= t ".client_id" %></th>
<td><code><%= @application.uid %></code></td>
</tr>
<% unless secret.blank? && Doorkeeper.config.application_secret_hashed? %>
<tr>
<th><%= t ".client_secret" %></th>
<td><code><%= secret %></code></td>
</tr>
<% end -%>
<tr>
<th><%= t ".permissions" %></th>
<td>
<ul class="list-unstyled mb-0">
<% @application.scopes.each do |scope| -%>
<li><%= t "oauth.scopes.#{scope}" %></li>
<% end -%>
</ul>
</td>
</tr>
<tr>
<th><%= t ".redirect_uris" %></th>
<td>
<ul class="list-unstyled mb-0">
<% @application.redirect_uri.split.each do |uri| -%>
<li><%= uri %></li>
<% end -%>
</ul>
</td>
</tr>
</table>
5 changes: 5 additions & 0 deletions app/views/oauth2_authorizations/error.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<% content_for :heading do %>
<h1><%= t ".title" %></h1>
<% end %>

<p><%= @pre_auth.error_response.body[:error_description] %></p>

0 comments on commit 475ecac

Please sign in to comment.