Skip to content
Extraits de code Groupes Projets
Valider a9e40a3d rédigé par Eugen Rochko's avatar Eugen Rochko
Parcourir les fichiers

Adding OAuth access scopes, fixing OAuth authorization UI, adding rate limiting

to the API
parent 17122df8
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 172 ajouts et 86 suppressions
Rails: Rails:
Enabled: true Enabled: true
Metrics/LineLength:
Enabled: false
Style/PerlBackrefs: Style/PerlBackrefs:
AutoCorrect: false AutoCorrect: false
Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
Enabled: false Enabled: false
Documentation: Metrics/BlockNesting:
Max: 2
Metrics/LineLength:
AllowURI: true
Enabled: false
Metrics/MethodLength:
CountComments: false
Max: 10
Metrics/ModuleLength:
Max: 100
Metrics/ParameterLists:
Max: 4
CountKeywordArgs: true
Style/AccessModifierIndentation:
EnforcedStyle: indent
Style/CollectionMethods:
Enabled: true
PreferredMethods:
find_all: 'select'
Style/Documentation:
Enabled: false
Style/DoubleNegation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false Enabled: false
Style/SpaceInsideHashLiteralBraces:
EnforcedStyle: space
Style/TrailingCommaInLiteral:
EnforcedStyleForMultiline: 'comma'
Style/RegexpLiteral:
Enabled: false
AllCops:
TargetRubyVersion: 2.2
Exclude:
- 'spec/**/*'
- 'db/**/*'
- 'app/views/**/*'
- 'config/**/*'
...@@ -85,18 +85,7 @@ ...@@ -85,18 +85,7 @@
} }
} }
.prompt { code {
font-size: 16px;
color: #9baec8;
text-align: center;
.prompt-highlight {
font-weight: 500;
color: #fff;
}
}
code.copypasteable {
display: block; display: block;
font-family: 'Roboto Mono', monospace; font-family: 'Roboto Mono', monospace;
font-weight: 400; font-weight: 400;
...@@ -110,42 +99,42 @@ ...@@ -110,42 +99,42 @@
.actions { .actions {
margin-top: 30px; margin-top: 30px;
}
button { button {
display: block; display: block;
width: 100%; width: 100%;
border: 0; border: 0;
border-radius: 4px; border-radius: 4px;
background: #2b90d9; background: #2b90d9;
color: #fff; color: #fff;
font-size: 18px; font-size: 18px;
padding: 10px; padding: 10px;
text-transform: uppercase; text-transform: uppercase;
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
outline: 0; outline: 0;
margin-bottom: 10px; margin-bottom: 10px;
&:hover { &:hover {
background-color: lighten(#2b90d9, 5%); background-color: lighten(#2b90d9, 5%);
} }
&:active, &:focus { &:active, &:focus {
position: relative; position: relative;
top: 1px; top: 1px;
background-color: darken(#2b90d9, 5%); background-color: darken(#2b90d9, 5%);
} }
&.negative { &.negative {
background: #df405a; background: #df405a;
&:hover { &:hover {
background-color: lighten(#df405a, 5%); background-color: lighten(#df405a, 5%);
} }
&:active, &:focus { &:active, &:focus {
background-color: darken(#df405a, 5%); background-color: darken(#df405a, 5%);
}
} }
} }
} }
...@@ -180,3 +169,18 @@ ...@@ -180,3 +169,18 @@
} }
} }
.oauth-prompt {
margin-bottom: 30px;
text-align: center;
color: #9baec8;
h2 {
font-size: 16px;
margin-bottom: 30px;
}
strong {
color: #d9e1e8;
font-weight: 500;
}
}
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
class PublicChannel < ApplicationCable::Channel class PublicChannel < ApplicationCable::Channel
def subscribed def subscribed
stream_from 'timeline:public', -> (encoded_message) do stream_from 'timeline:public', lambda do |encoded_message|
message = ActiveSupport::JSON.decode(encoded_message) message = ActiveSupport::JSON.decode(encoded_message)
status = Status.find_by(id: message['id']) status = Status.find_by(id: message['id'])
......
class Api::V1::AccountsController < ApiController class Api::V1::AccountsController < ApiController
before_action :doorkeeper_authorize! before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock]
before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock]
before_action :set_account, except: [:verify_credentials, :suggestions] before_action :set_account, except: [:verify_credentials, :suggestions]
respond_to :json respond_to :json
......
class Api::V1::FollowsController < ApiController class Api::V1::FollowsController < ApiController
before_action :doorkeeper_authorize! before_action -> { doorkeeper_authorize! :follow }
respond_to :json respond_to :json
def create def create
......
class Api::V1::MediaController < ApiController class Api::V1::MediaController < ApiController
before_action :doorkeeper_authorize! before_action -> { doorkeeper_authorize! :write }
respond_to :json respond_to :json
def create def create
......
class Api::V1::StatusesController < ApiController class Api::V1::StatusesController < ApiController
before_action :doorkeeper_authorize! before_action -> { doorkeeper_authorize! :read }, except: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite]
before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite]
respond_to :json respond_to :json
def show def show
......
class ApiController < ApplicationController class ApiController < ApplicationController
protect_from_forgery with: :null_session protect_from_forgery with: :null_session
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
before_action :set_rate_limit_headers
rescue_from ActiveRecord::RecordInvalid do |e| rescue_from ActiveRecord::RecordInvalid do |e|
render json: { error: e.to_s }, status: 422 render json: { error: e.to_s }, status: 422
end end
...@@ -22,8 +25,27 @@ class ApiController < ApplicationController ...@@ -22,8 +25,27 @@ class ApiController < ApplicationController
render json: { error: 'Remote SSL certificate could not be verified' }, status: 503 render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
end end
def doorkeeper_unauthorized_render_options(*)
{ json: { error: 'Not authorized' } }
end
def doorkeeper_forbidden_render_options(*)
{ json: { error: 'This action is outside the authorized scopes' } }
end
protected protected
def set_rate_limit_headers
return if request.env['rack.attack.throttle_data'].nil?
now = Time.now.utc
match_data = request.env['rack.attack.throttle_data']['api']
response.headers['X-RateLimit-Limit'] = match_data[:limit].to_s
response.headers['X-RateLimit-Remaining'] = (match_data[:limit] - match_data[:count]).to_s
response.headers['X-RateLimit-Reset'] = (now + (match_data[:period] - now.to_i % match_data[:period])).to_s
end
def current_resource_owner def current_resource_owner
User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end end
......
...@@ -15,6 +15,6 @@ class HomeController < ApplicationController ...@@ -15,6 +15,6 @@ class HomeController < ApplicationController
end end
def find_or_create_access_token def find_or_create_access_token
Doorkeeper::AccessToken.find_or_create_for(Doorkeeper::Application.where(superapp: true).first, current_user.id, nil, Doorkeeper.configuration.access_token_expires_in, Doorkeeper.configuration.refresh_token_enabled?) Doorkeeper::AccessToken.find_or_create_for(Doorkeeper::Application.where(superapp: true).first, current_user.id, 'read write follow', Doorkeeper.configuration.access_token_expires_in, Doorkeeper.configuration.refresh_token_enabled?)
end end
end end
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :store_current_location
private
def store_current_location
store_location_for(:user, request.url)
end
end
...@@ -7,7 +7,7 @@ class Feed ...@@ -7,7 +7,7 @@ class Feed
def get(limit, max_id = nil, since_id = nil) def get(limit, max_id = nil, since_id = nil)
max_id = '+inf' if max_id.blank? max_id = '+inf' if max_id.blank?
since_id = '-inf' if since_id.blank? since_id = '-inf' if since_id.blank?
unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).collect(&:last).map(&:to_i) unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:last).map(&:to_i)
# If we're after most recent items and none are there, we need to precompute the feed # If we're after most recent items and none are there, we need to precompute the feed
if unhydrated.empty? && max_id == '+inf' && since_id == '-inf' if unhydrated.empty? && max_id == '+inf' && since_id == '-inf'
......
...@@ -34,7 +34,8 @@ class MediaAttachment < ApplicationRecord ...@@ -34,7 +34,8 @@ class MediaAttachment < ApplicationRecord
image? ? 'image' : 'video' image? ? 'image' : 'video'
end end
private private
def self.file_styles(f) def self.file_styles(f)
if f.instance.image? if f.instance.image?
{ {
......
.prompt= raw t('.prompt', client_name: "<strong class=\"prompt-highlight\">#{ @pre_auth.client.name }</strong>")
/- if @pre_auth.scopes.count > 0
/ .scope-permission-prompt
/ %p= t('.able_to')
/ %ul.scope-permissions
/ - @pre_auth.scopes.each do |scope|
/ %li= t scope, scope: [:doorkeeper, :scopes]
.actions
= form_tag oauth_authorization_path, method: :post do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
= hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= button_tag t('doorkeeper.authorizations.buttons.authorize'), type: :submit
= form_tag oauth_authorization_path, method: :delete do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
= hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= button_tag t('doorkeeper.authorizations.buttons.deny'), type: :submit, class: 'negative'
.prompt= t('.title')
%code.copypasteable= params[:code]
.prompt= t('doorkeeper.authorizations.error.title') .flash-message#error_explanation
#error_explanation
= @pre_auth.error_response.body[:error_description] = @pre_auth.error_response.body[:error_description]
.oauth-prompt
%h2
Application
%strong=@pre_auth.client.name
requests access to your account
%p
It will be able to
= @pre_auth.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.map { |s| "<strong>#{s}</strong>"}.to_sentence.html_safe
= form_tag oauth_authorization_path, method: :post, class: 'simple_form' do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
= hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= button_tag t('doorkeeper.authorizations.buttons.authorize'), type: :submit
= form_tag oauth_authorization_path, method: :delete, class: 'simple_form' do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
= hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= button_tag t('doorkeeper.authorizations.buttons.deny'), type: :submit, class: 'negative'
%code= params[:code]
...@@ -12,7 +12,7 @@ Rails.application.configure do ...@@ -12,7 +12,7 @@ Rails.application.configure do
# Full error reports are disabled and caching is turned on. # Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false config.consider_all_requests_local = false
config.action_controller.perform_caching = false config.action_controller.perform_caching = true
# Disable serving static files from the `/public` folder by default since # Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this. # Apache or NGINX already handles this.
......
...@@ -50,8 +50,8 @@ Doorkeeper.configure do ...@@ -50,8 +50,8 @@ Doorkeeper.configure do
# Define access token scopes for your provider # Define access token scopes for your provider
# For more information go to # For more information go to
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
# default_scopes :public default_scopes :read
# optional_scopes :write, :follow optional_scopes :write, :follow
# Change the way client credentials are retrieved from the request object. # Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
......
Rabl.configure do |config| Rabl.configure do |config|
config.cache_all_output = true config.cache_all_output = false
config.cache_sources = !!Rails.env.production? config.cache_sources = !!Rails.env.production?
config.include_json_root = false config.include_json_root = false
config.view_paths = [Rails.root.join('app/views')] config.view_paths = [Rails.root.join('app/views')]
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter