mirror of
https://github.com/mastodon/mastodon.git
synced 2024-12-22 22:15:23 +01:00
Adding doorkeeper, adding a REST API
POST /api/statuses Params: status (text contents), in_reply_to_id (optional) GET /api/statuses/:id POST /api/statuses/:id/reblog GET /api/accounts/:id GET /api/accounts/:id/following GET /api/accounts/:id/followers POST /api/accounts/:id/follow POST /api/accounts/:id/unfollow POST /api/follows Params: uri (e.g. user@domain) OAuth authentication is currently disabled, but the API can be used with HTTP Auth.
This commit is contained in:
parent
3824c58853
commit
ab6696e855
54 changed files with 847 additions and 61 deletions
3
Gemfile
3
Gemfile
|
@ -27,6 +27,9 @@ gem 'ostatus2'
|
||||||
gem 'goldfinger'
|
gem 'goldfinger'
|
||||||
gem 'devise'
|
gem 'devise'
|
||||||
gem 'rails_autolink'
|
gem 'rails_autolink'
|
||||||
|
gem 'doorkeeper'
|
||||||
|
gem 'rabl'
|
||||||
|
gem 'oj'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
|
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -74,8 +74,10 @@ GEM
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
diff-lcs (1.2.5)
|
diff-lcs (1.2.5)
|
||||||
docile (1.1.5)
|
docile (1.1.5)
|
||||||
domain_name (0.5.20160128)
|
domain_name (0.5.20160216)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
doorkeeper (3.1.0)
|
||||||
|
railties (>= 3.2)
|
||||||
dotenv (2.1.0)
|
dotenv (2.1.0)
|
||||||
dotenv-rails (2.1.0)
|
dotenv-rails (2.1.0)
|
||||||
dotenv (= 2.1.0)
|
dotenv (= 2.1.0)
|
||||||
|
@ -90,7 +92,7 @@ GEM
|
||||||
ruby-progressbar (~> 1.4)
|
ruby-progressbar (~> 1.4)
|
||||||
globalid (0.3.6)
|
globalid (0.3.6)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
goldfinger (1.0.1)
|
goldfinger (1.0.2)
|
||||||
addressable (~> 2.4)
|
addressable (~> 2.4)
|
||||||
http (~> 1.0)
|
http (~> 1.0)
|
||||||
nokogiri (~> 1.6)
|
nokogiri (~> 1.6)
|
||||||
|
@ -139,6 +141,7 @@ GEM
|
||||||
multi_json (1.11.2)
|
multi_json (1.11.2)
|
||||||
nokogiri (1.6.7.2)
|
nokogiri (1.6.7.2)
|
||||||
mini_portile2 (~> 2.0.0.rc2)
|
mini_portile2 (~> 2.0.0.rc2)
|
||||||
|
oj (2.14.5)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostatus2 (0.1.1)
|
ostatus2 (0.1.1)
|
||||||
addressable (~> 2.4)
|
addressable (~> 2.4)
|
||||||
|
@ -165,6 +168,8 @@ GEM
|
||||||
puma (2.16.0)
|
puma (2.16.0)
|
||||||
quiet_assets (1.1.0)
|
quiet_assets (1.1.0)
|
||||||
railties (>= 3.1, < 5.0)
|
railties (>= 3.1, < 5.0)
|
||||||
|
rabl (0.12.0)
|
||||||
|
activesupport (>= 2.3.14)
|
||||||
rack (1.6.4)
|
rack (1.6.4)
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
|
@ -300,6 +305,7 @@ DEPENDENCIES
|
||||||
binding_of_caller
|
binding_of_caller
|
||||||
coffee-rails (~> 4.1.0)
|
coffee-rails (~> 4.1.0)
|
||||||
devise
|
devise
|
||||||
|
doorkeeper
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
fabrication
|
fabrication
|
||||||
font-awesome-sass
|
font-awesome-sass
|
||||||
|
@ -310,6 +316,7 @@ DEPENDENCIES
|
||||||
jbuilder (~> 2.0)
|
jbuilder (~> 2.0)
|
||||||
jquery-rails
|
jquery-rails
|
||||||
nokogiri
|
nokogiri
|
||||||
|
oj
|
||||||
ostatus2
|
ostatus2
|
||||||
paperclip (~> 4.3)
|
paperclip (~> 4.3)
|
||||||
paranoia (~> 2.0)
|
paranoia (~> 2.0)
|
||||||
|
@ -317,6 +324,7 @@ DEPENDENCIES
|
||||||
pry-rails
|
pry-rails
|
||||||
puma
|
puma
|
||||||
quiet_assets
|
quiet_assets
|
||||||
|
rabl
|
||||||
rails (= 4.2.5.1)
|
rails (= 4.2.5.1)
|
||||||
rails_12factor
|
rails_12factor
|
||||||
rails_autolink
|
rails_autolink
|
||||||
|
|
3
app/assets/javascripts/api/accounts.coffee
Normal file
3
app/assets/javascripts/api/accounts.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/follows.coffee
Normal file
3
app/assets/javascripts/api/follows.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/statuses.coffee
Normal file
3
app/assets/javascripts/api/statuses.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/stylesheets/api/accounts.scss
Normal file
3
app/assets/stylesheets/api/accounts.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the Api::Accounts controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/follows.scss
Normal file
3
app/assets/stylesheets/api/follows.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the API::Follows controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/statuses.scss
Normal file
3
app/assets/stylesheets/api/statuses.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the API::Statuses controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -3,7 +3,6 @@ class AccountsController < ApplicationController
|
||||||
|
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
before_action :set_webfinger_header
|
before_action :set_webfinger_header
|
||||||
before_action :authenticate_user!, only: [:follow, :unfollow]
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = @account.statuses.order('id desc').includes(thread: [:account], reblog: [:account], stream_entry: [])
|
@statuses = @account.statuses.order('id desc').includes(thread: [:account], reblog: [:account], stream_entry: [])
|
||||||
|
@ -14,16 +13,6 @@ class AccountsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow
|
|
||||||
current_user.account.follow!(@account)
|
|
||||||
redirect_to root_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def unfollow
|
|
||||||
current_user.account.unfollow!(@account)
|
|
||||||
redirect_to root_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_account
|
def set_account
|
||||||
|
|
36
app/controllers/api/accounts_controller.rb
Normal file
36
app/controllers/api/accounts_controller.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
class Api::AccountsController < ApiController
|
||||||
|
before_action :set_account
|
||||||
|
before_action :authenticate_user!
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def following
|
||||||
|
@following = @account.following
|
||||||
|
end
|
||||||
|
|
||||||
|
def followers
|
||||||
|
@followers = @account.followers
|
||||||
|
end
|
||||||
|
|
||||||
|
def statuses
|
||||||
|
@statuses = @account.statuses
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow
|
||||||
|
@follow = current_user.account.follow!(@account)
|
||||||
|
render action: :show
|
||||||
|
end
|
||||||
|
|
||||||
|
def unfollow
|
||||||
|
@unfollow = current_user.account.unfollow!(@account)
|
||||||
|
render action: :show
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
9
app/controllers/api/follows_controller.rb
Normal file
9
app/controllers/api/follows_controller.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class Api::FollowsController < ApiController
|
||||||
|
before_action :authenticate_user!
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def create
|
||||||
|
@follow = FollowService.new.(current_user.account, params[:uri])
|
||||||
|
render action: :show
|
||||||
|
end
|
||||||
|
end
|
18
app/controllers/api/statuses_controller.rb
Normal file
18
app/controllers/api/statuses_controller.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
class Api::StatusesController < ApiController
|
||||||
|
before_action :authenticate_user!
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def show
|
||||||
|
@status = Status.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@status = PostStatusService.new.(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]))
|
||||||
|
render action: :show
|
||||||
|
end
|
||||||
|
|
||||||
|
def reblog
|
||||||
|
@status = ReblogService.new.(current_user.account, Status.find(params[:id]))
|
||||||
|
render action: :show
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,13 @@
|
||||||
class ApiController < ApplicationController
|
class ApiController < ApplicationController
|
||||||
protect_from_forgery with: :null_session
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def current_resource_owner
|
||||||
|
User.find(doorkeeper_token.user_id) if doorkeeper_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_user
|
||||||
|
super || current_resource_owner
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,6 @@ class StreamEntriesController < ApplicationController
|
||||||
|
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
before_action :set_stream_entry
|
before_action :set_stream_entry
|
||||||
before_action :authenticate_user!, only: [:reblog, :favourite]
|
|
||||||
before_action :only_statuses!, only: [:reblog, :favourite]
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@type = @stream_entry.activity_type.downcase
|
@type = @stream_entry.activity_type.downcase
|
||||||
|
@ -15,16 +13,6 @@ class StreamEntriesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def reblog
|
|
||||||
ReblogService.new.(current_user.account, @stream_entry.activity)
|
|
||||||
redirect_to root_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def favourite
|
|
||||||
FavouriteService.new.(current_user.account, @stream_entry.activity)
|
|
||||||
redirect_to root_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_account
|
def set_account
|
||||||
|
@ -34,8 +22,4 @@ class StreamEntriesController < ApplicationController
|
||||||
def set_stream_entry
|
def set_stream_entry
|
||||||
@stream_entry = @account.stream_entries.find(params[:id])
|
@stream_entry = @account.stream_entries.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def only_statuses!
|
|
||||||
redirect_to root_url unless @stream_entry.activity_type == 'Status'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
2
app/helpers/api/accounts_helper.rb
Normal file
2
app/helpers/api/accounts_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::AccountsHelper
|
||||||
|
end
|
2
app/helpers/api/follows_helper.rb
Normal file
2
app/helpers/api/follows_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::FollowsHelper
|
||||||
|
end
|
2
app/helpers/api/statuses_helper.rb
Normal file
2
app/helpers/api/statuses_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::StatusesHelper
|
||||||
|
end
|
|
@ -31,10 +31,10 @@ module StreamEntriesHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def reblogged_by_me_class(status)
|
def reblogged_by_me_class(status)
|
||||||
user_signed_in? && (status.reblog? ? status.reblog : status).reblogs.where(account: current_user.account).count == 1 ? 'reblogged' : ''
|
user_signed_in? && current_user.account.reblogged?(status) ? 'reblogged' : ''
|
||||||
end
|
end
|
||||||
|
|
||||||
def favourited_by_me_class(status)
|
def favourited_by_me_class(status)
|
||||||
user_signed_in? && (status.reblog? ? status.reblog : status).favourites.where(account: current_user.account).count == 1 ? 'favourited' : ''
|
user_signed_in? && current_user.account.favourited?(status) ? 'favourited' : ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Account < ActiveRecord::Base
|
||||||
MENTION_RE = /(?:^|\W)@([a-z0-9_]+(?:@[a-z0-9\.\-]+)?)/i
|
MENTION_RE = /(?:^|\W)@([a-z0-9_]+(?:@[a-z0-9\.\-]+)?)/i
|
||||||
|
|
||||||
def follow!(other_account)
|
def follow!(other_account)
|
||||||
self.active_relationships.first_or_create!(target_account: other_account)
|
self.active_relationships.where(target_account: other_account).first_or_create!(target_account: other_account)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfollow!(other_account)
|
def unfollow!(other_account)
|
||||||
|
@ -59,6 +59,14 @@ class Account < ActiveRecord::Base
|
||||||
!(self.secret.blank? || self.verify_token.blank?)
|
!(self.secret.blank? || self.verify_token.blank?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def favourited?(status)
|
||||||
|
(status.reblog? ? status.reblog : status).favourites.where(account: self).count == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def reblogged?(status)
|
||||||
|
(status.reblog? ? status.reblog : status).reblogs.where(account: self).count == 1
|
||||||
|
end
|
||||||
|
|
||||||
def keypair
|
def keypair
|
||||||
self.private_key.nil? ? OpenSSL::PKey::RSA.new(self.public_key) : OpenSSL::PKey::RSA.new(self.private_key)
|
self.private_key.nil? ? OpenSSL::PKey::RSA.new(self.public_key) : OpenSSL::PKey::RSA.new(self.private_key)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,11 +5,12 @@ class FollowService < BaseService
|
||||||
def call(source_account, uri)
|
def call(source_account, uri)
|
||||||
target_account = follow_remote_account_service.(uri)
|
target_account = follow_remote_account_service.(uri)
|
||||||
|
|
||||||
return if target_account.nil?
|
return nil if target_account.nil?
|
||||||
|
|
||||||
follow = source_account.follow!(target_account)
|
follow = source_account.follow!(target_account)
|
||||||
send_interaction_service.(follow.stream_entry, target_account)
|
send_interaction_service.(follow.stream_entry, target_account)
|
||||||
source_account.ping!(account_url(account, format: 'atom'), [Rails.configuration.x.hub_url])
|
source_account.ping!(account_url(source_account, format: 'atom'), [Rails.configuration.x.hub_url])
|
||||||
|
follow
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
2
app/views/api/accounts/followers.rabl
Normal file
2
app/views/api/accounts/followers.rabl
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
collection @followers
|
||||||
|
extends('api/accounts/show')
|
2
app/views/api/accounts/following.rabl
Normal file
2
app/views/api/accounts/following.rabl
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
collection @following
|
||||||
|
extends('api/accounts/show')
|
9
app/views/api/accounts/show.rabl
Normal file
9
app/views/api/accounts/show.rabl
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
object @account
|
||||||
|
|
||||||
|
attributes :id, :username, :acct, :display_name, :note
|
||||||
|
|
||||||
|
node(:url) { |account| url_for_target(account) }
|
||||||
|
node(:avatar) { |account| asset_url(account.avatar.url(:large, false)) }
|
||||||
|
node(:followers) { |account| account.followers.count }
|
||||||
|
node(:following) { |account| account.following.count }
|
||||||
|
node(:statuses) { |account| account.statuses.count }
|
2
app/views/api/accounts/statuses.rabl
Normal file
2
app/views/api/accounts/statuses.rabl
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
collection @statuses
|
||||||
|
extends('api/statuses/show')
|
5
app/views/api/follows/show.rabl
Normal file
5
app/views/api/follows/show.rabl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
object @follow
|
||||||
|
|
||||||
|
child :target_account => :target_account do
|
||||||
|
extends('api/accounts/show')
|
||||||
|
end
|
18
app/views/api/statuses/show.rabl
Normal file
18
app/views/api/statuses/show.rabl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
object @status
|
||||||
|
attributes :id, :created_at, :in_reply_to_id
|
||||||
|
|
||||||
|
node(:uri) { |status| uri_for_target(status) }
|
||||||
|
node(:content) { |status| status.local? ? linkify(status) : status.content }
|
||||||
|
node(:url) { |status| url_for_target(status) }
|
||||||
|
node(:reblogs) { |status| status.reblogs.count }
|
||||||
|
node(:favourites) { |status| status.favourites.count }
|
||||||
|
node(:favourited) { |status| current_user.account.favourited?(status) }
|
||||||
|
node(:reblogged) { |status| current_user.account.reblogged?(status) }
|
||||||
|
|
||||||
|
child :reblog => :reblog do
|
||||||
|
extends('api/statuses/show')
|
||||||
|
end
|
||||||
|
|
||||||
|
child :account do
|
||||||
|
extends('api/accounts/show')
|
||||||
|
end
|
5
app/views/doorkeeper/applications/_delete_form.html.erb
Normal file
5
app/views/doorkeeper/applications/_delete_form.html.erb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<%- submit_btn_css ||= 'btn btn-link' %>
|
||||||
|
<%= form_tag oauth_application_path(application) do %>
|
||||||
|
<input type="hidden" name="_method" value="delete">
|
||||||
|
<%= submit_tag t('doorkeeper.applications.buttons.destroy'), onclick: "return confirm('#{ t('doorkeeper.applications.confirmations.destroy') }')", class: submit_btn_css %>
|
||||||
|
<% end %>
|
47
app/views/doorkeeper/applications/_form.html.erb
Normal file
47
app/views/doorkeeper/applications/_form.html.erb
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<%= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f| %>
|
||||||
|
<% if application.errors.any? %>
|
||||||
|
<div class="alert alert-danger" data-alert><p><%= t('doorkeeper.applications.form.error') %></p></div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do %>
|
||||||
|
<%= f.label :name, class: 'col-sm-2 control-label' %>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<%= f.text_field :name, class: 'form-control' %>
|
||||||
|
<%= doorkeeper_errors_for application, :name %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do %>
|
||||||
|
<%= f.label :redirect_uri, class: 'col-sm-2 control-label' %>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<%= f.text_area :redirect_uri, class: 'form-control' %>
|
||||||
|
<%= doorkeeper_errors_for application, :redirect_uri %>
|
||||||
|
<span class="help-block">
|
||||||
|
<%= t('doorkeeper.applications.help.redirect_uri') %>
|
||||||
|
</span>
|
||||||
|
<% if Doorkeeper.configuration.native_redirect_uri %>
|
||||||
|
<span class="help-block">
|
||||||
|
<%= raw t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: "<code>#{ Doorkeeper.configuration.native_redirect_uri }</code>") %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= content_tag :div, class: "form-group#{' has-error' if application.errors[:scopes].present?}" do %>
|
||||||
|
<%= f.label :scopes, class: 'col-sm-2 control-label' %>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<%= f.text_field :scopes, class: 'form-control' %>
|
||||||
|
<%= doorkeeper_errors_for application, :scopes %>
|
||||||
|
<span class="help-block">
|
||||||
|
<%= t('doorkeeper.applications.help.scopes') %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<%= f.submit t('doorkeeper.applications.buttons.submit'), class: "btn btn-primary" %>
|
||||||
|
<%= link_to t('doorkeeper.applications.buttons.cancel'), oauth_applications_path, :class => "btn btn-default" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
5
app/views/doorkeeper/applications/edit.html.erb
Normal file
5
app/views/doorkeeper/applications/edit.html.erb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<h1><%= t('.title') %></h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render 'form', application: @application %>
|
26
app/views/doorkeeper/applications/index.html.erb
Normal file
26
app/views/doorkeeper/applications/index.html.erb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<h1><%= t('.title') %></h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p><%= link_to t('.new'), new_oauth_application_path, class: 'btn btn-success' %></p>
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><%= t('.name') %></th>
|
||||||
|
<th><%= t('.callback_url') %></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @applications.each do |application| %>
|
||||||
|
<tr id="application_<%= application.id %>">
|
||||||
|
<td><%= link_to application.name, oauth_application_path(application) %></td>
|
||||||
|
<td><%= application.redirect_uri %></td>
|
||||||
|
<td><%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(application), class: 'btn btn-link' %></td>
|
||||||
|
<td><%= render 'delete_form', application: application %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
5
app/views/doorkeeper/applications/new.html.erb
Normal file
5
app/views/doorkeeper/applications/new.html.erb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<h1><%= t('.title') %></h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render 'form', application: @application %>
|
39
app/views/doorkeeper/applications/show.html.erb
Normal file
39
app/views/doorkeeper/applications/show.html.erb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<h1><%= t('.title', name: @application.name) %></h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h4><%= t('.application_id') %>:</h4>
|
||||||
|
<p><code id="application_id"><%= @application.uid %></code></p>
|
||||||
|
|
||||||
|
<h4><%= t('.secret') %>:</h4>
|
||||||
|
<p><code id="secret"><%= @application.secret %></code></p>
|
||||||
|
|
||||||
|
<h4><%= t('.scopes') %>:</h4>
|
||||||
|
<p><code id="scopes"><%= @application.scopes %></code></p>
|
||||||
|
|
||||||
|
<h4><%= t('.callback_urls') %>:</h4>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<% @application.redirect_uri.split.each do |uri| %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code><%= uri %></code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= link_to t('doorkeeper.applications.buttons.authorize'), oauth_authorization_path(client_id: @application.uid, redirect_uri: uri, response_type: 'code'), class: 'btn btn-success', target: '_blank' %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h3><%= t('.actions') %></h3>
|
||||||
|
|
||||||
|
<p><%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(@application), class: 'btn btn-primary' %></p>
|
||||||
|
|
||||||
|
<p><%= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger' %></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
7
app/views/doorkeeper/authorizations/error.html.erb
Normal file
7
app/views/doorkeeper/authorizations/error.html.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="page-header">
|
||||||
|
<h1><%= t('doorkeeper.authorizations.error.title') %></h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<pre><%= @pre_auth.error_response.body[:error_description] %></pre>
|
||||||
|
</main>
|
40
app/views/doorkeeper/authorizations/new.html.erb
Normal file
40
app/views/doorkeeper/authorizations/new.html.erb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<header class="page-header" role="banner">
|
||||||
|
<h1><%= t('.title') %></h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<p class="h4">
|
||||||
|
<%= raw t('.prompt', client_name: "<strong class=\"text-info\">#{ @pre_auth.client.name }</strong>") %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<% if @pre_auth.scopes.count > 0 %>
|
||||||
|
<div id="oauth-permissions">
|
||||||
|
<p><%= t('.able_to') %>:</p>
|
||||||
|
|
||||||
|
<ul class="text-info">
|
||||||
|
<% @pre_auth.scopes.each do |scope| %>
|
||||||
|
<li><%= t scope, scope: [:doorkeeper, :scopes] %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="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 %>
|
||||||
|
<%= submit_tag t('doorkeeper.authorizations.buttons.authorize'), class: "btn btn-success btn-lg btn-block" %>
|
||||||
|
<% end %>
|
||||||
|
<%= 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 %>
|
||||||
|
<%= submit_tag t('doorkeeper.authorizations.buttons.deny'), class: "btn btn-danger btn-lg btn-block" %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</main>
|
7
app/views/doorkeeper/authorizations/show.html.erb
Normal file
7
app/views/doorkeeper/authorizations/show.html.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<header class="page-header">
|
||||||
|
<h1><%= t('.title') %>:</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<code id="authorization_code"><%= params[:code] %></code>
|
||||||
|
</main>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<%- submit_btn_css ||= 'btn btn-link' %>
|
||||||
|
<%= form_tag oauth_authorized_application_path(application) do %>
|
||||||
|
<input type="hidden" name="_method" value="delete">
|
||||||
|
<%= submit_tag t('doorkeeper.authorized_applications.buttons.revoke'), onclick: "return confirm('#{ t('doorkeeper.authorized_applications.confirmations.revoke') }')", class: submit_btn_css %>
|
||||||
|
<% end %>
|
25
app/views/doorkeeper/authorized_applications/index.html.erb
Normal file
25
app/views/doorkeeper/authorized_applications/index.html.erb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<header class="page-header">
|
||||||
|
<h1><%= t('doorkeeper.authorized_applications.index.title') %></h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><%= t('doorkeeper.authorized_applications.index.application') %></th>
|
||||||
|
<th><%= t('doorkeeper.authorized_applications.index.created_at') %></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @applications.each do |application| %>
|
||||||
|
<tr>
|
||||||
|
<td><%= application.name %></td>
|
||||||
|
<td><%= application.created_at.strftime(t('doorkeeper.authorized_applications.index.date_format')) %></td>
|
||||||
|
<td><%= render 'delete_form', application: application %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
37
app/views/layouts/doorkeeper/admin.html.erb
Normal file
37
app/views/layouts/doorkeeper/admin.html.erb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Doorkeeper</title>
|
||||||
|
<%= stylesheet_link_tag "doorkeeper/admin/application" %>
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="navbar navbar-inverse navbar-static-top" role="navigation">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<%= link_to t('doorkeeper.layouts.admin.nav.oauth2_provider'), oauth_applications_path, class: 'navbar-brand' %>
|
||||||
|
</div>
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<%= content_tag :li, class: "#{'active' if request.path == oauth_applications_path}" do %>
|
||||||
|
<%= link_to t('doorkeeper.layouts.admin.nav.applications'), oauth_applications_path %>
|
||||||
|
<% end %>
|
||||||
|
<%= content_tag :li do %>
|
||||||
|
<%= link_to 'Home', root_path %>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<%- if flash[:notice].present? %>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<%= flash[:notice] %>
|
||||||
|
</div>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
23
app/views/layouts/doorkeeper/application.html.erb
Normal file
23
app/views/layouts/doorkeeper/application.html.erb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= t('doorkeeper.layouts.application.title') %></title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<%= stylesheet_link_tag "doorkeeper/application" %>
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<%- if flash[:notice].present? %>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<%= flash[:notice] %>
|
||||||
|
</div>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -27,5 +27,10 @@ module Mastodon
|
||||||
|
|
||||||
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
|
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
|
||||||
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
||||||
|
|
||||||
|
config.to_prepare do
|
||||||
|
Doorkeeper::AuthorizationsController.layout 'auth'
|
||||||
|
Doorkeeper::AuthorizedApplicationsController.layout 'auth'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -61,7 +61,7 @@ Devise.setup do |config|
|
||||||
# given strategies, for example, `config.http_authenticatable = [:database]` will
|
# given strategies, for example, `config.http_authenticatable = [:database]` will
|
||||||
# enable it only for database authentication. The supported strategies are:
|
# enable it only for database authentication. The supported strategies are:
|
||||||
# :database = Support basic authentication with authentication key + password
|
# :database = Support basic authentication with authentication key + password
|
||||||
# config.http_authenticatable = false
|
config.http_authenticatable = [:database]
|
||||||
|
|
||||||
# If 401 status code should be returned for AJAX requests. True by default.
|
# If 401 status code should be returned for AJAX requests. True by default.
|
||||||
# config.http_authenticatable_on_xhr = true
|
# config.http_authenticatable_on_xhr = true
|
||||||
|
|
104
config/initializers/doorkeeper.rb
Normal file
104
config/initializers/doorkeeper.rb
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
Doorkeeper.configure do
|
||||||
|
# Change the ORM that doorkeeper will use (needs plugins)
|
||||||
|
orm :active_record
|
||||||
|
|
||||||
|
# This block will be called to check whether the resource owner is authenticated or not.
|
||||||
|
resource_owner_authenticator do
|
||||||
|
current_user || redirect_to(new_user_session_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
|
||||||
|
# admin_authenticator do
|
||||||
|
# # Put your admin authentication logic here.
|
||||||
|
# # Example implementation:
|
||||||
|
# Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Authorization Code expiration time (default 10 minutes).
|
||||||
|
# authorization_code_expires_in 10.minutes
|
||||||
|
|
||||||
|
# Access token expiration time (default 2 hours).
|
||||||
|
# If you want to disable expiration, set this to nil.
|
||||||
|
# access_token_expires_in 2.hours
|
||||||
|
|
||||||
|
# Assign a custom TTL for implicit grants.
|
||||||
|
# custom_access_token_expires_in do |oauth_client|
|
||||||
|
# oauth_client.application.additional_settings.implicit_oauth_expiration
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Use a custom class for generating the access token.
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
|
||||||
|
# access_token_generator "::Doorkeeper::JWT"
|
||||||
|
|
||||||
|
# Reuse access token for the same resource owner within an application (disabled by default)
|
||||||
|
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
|
||||||
|
# reuse_access_token
|
||||||
|
|
||||||
|
# Issue access tokens with refresh token (disabled by default)
|
||||||
|
# use_refresh_token
|
||||||
|
|
||||||
|
# Provide support for an owner to be assigned to each registered application (disabled by default)
|
||||||
|
# Optional parameter :confirmation => true (default false) if you want to enforce ownership of
|
||||||
|
# a registered application
|
||||||
|
# Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
|
||||||
|
# enable_application_owner :confirmation => false
|
||||||
|
|
||||||
|
# Define access token scopes for your provider
|
||||||
|
# For more information go to
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
|
||||||
|
# default_scopes :public
|
||||||
|
# optional_scopes :write, :update
|
||||||
|
|
||||||
|
# Change the way client credentials are retrieved from the request object.
|
||||||
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
||||||
|
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
|
||||||
|
# Check out the wiki for more information on customization
|
||||||
|
# client_credentials :from_basic, :from_params
|
||||||
|
|
||||||
|
# Change the way access token is authenticated from the request object.
|
||||||
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
||||||
|
# falls back to the `:access_token` or `:bearer_token` params from the `params` object.
|
||||||
|
# Check out the wiki for more information on customization
|
||||||
|
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
|
||||||
|
|
||||||
|
# Change the native redirect uri for client apps
|
||||||
|
# When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
|
||||||
|
# The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
|
||||||
|
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
|
||||||
|
#
|
||||||
|
# native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
|
||||||
|
|
||||||
|
# Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
|
||||||
|
# by default in non-development environments). OAuth2 delegates security in
|
||||||
|
# communication to the HTTPS protocol so it is wise to keep this enabled.
|
||||||
|
#
|
||||||
|
# force_ssl_in_redirect_uri !Rails.env.development?
|
||||||
|
|
||||||
|
# Specify what grant flows are enabled in array of Strings. The valid
|
||||||
|
# strings and the flows they enable are:
|
||||||
|
#
|
||||||
|
# "authorization_code" => Authorization Code Grant Flow
|
||||||
|
# "implicit" => Implicit Grant Flow
|
||||||
|
# "password" => Resource Owner Password Credentials Grant Flow
|
||||||
|
# "client_credentials" => Client Credentials Grant Flow
|
||||||
|
#
|
||||||
|
# If not specified, Doorkeeper enables authorization_code and
|
||||||
|
# client_credentials.
|
||||||
|
#
|
||||||
|
# implicit and password grant flows have risks that you should understand
|
||||||
|
# before enabling:
|
||||||
|
# http://tools.ietf.org/html/rfc6819#section-4.4.2
|
||||||
|
# http://tools.ietf.org/html/rfc6819#section-4.4.3
|
||||||
|
#
|
||||||
|
# grant_flows %w(authorization_code client_credentials)
|
||||||
|
|
||||||
|
# Under some circumstances you might want to have applications auto-approved,
|
||||||
|
# so that the user skips the authorization step.
|
||||||
|
# For example if dealing with a trusted application.
|
||||||
|
# skip_authorization do |resource_owner, client|
|
||||||
|
# client.superapp? or resource_owner.admin?
|
||||||
|
# end
|
||||||
|
|
||||||
|
# WWW-Authenticate Realm (default "Doorkeeper").
|
||||||
|
# realm "Doorkeeper"
|
||||||
|
end
|
3
config/initializers/rabl_init.rb
Normal file
3
config/initializers/rabl_init.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Rabl.configure do |config|
|
||||||
|
config.include_json_root = false
|
||||||
|
end
|
|
@ -1,13 +0,0 @@
|
||||||
if Rails.env.development?
|
|
||||||
ActiveSupport::Dependencies.explicitly_unloadable_constants << 'Twitter::API'
|
|
||||||
|
|
||||||
api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
|
|
||||||
|
|
||||||
api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
|
|
||||||
Rails.application.reload_routes!
|
|
||||||
end
|
|
||||||
|
|
||||||
ActionDispatch::Callbacks.to_prepare do
|
|
||||||
api_reloader.execute_if_updated
|
|
||||||
end
|
|
||||||
end
|
|
123
config/locales/doorkeeper.en.yml
Normal file
123
config/locales/doorkeeper.en.yml
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
en:
|
||||||
|
activerecord:
|
||||||
|
attributes:
|
||||||
|
doorkeeper/application:
|
||||||
|
name: 'Name'
|
||||||
|
redirect_uri: 'Redirect URI'
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
doorkeeper/application:
|
||||||
|
attributes:
|
||||||
|
redirect_uri:
|
||||||
|
fragment_present: 'cannot contain a fragment.'
|
||||||
|
invalid_uri: 'must be a valid URI.'
|
||||||
|
relative_uri: 'must be an absolute URI.'
|
||||||
|
secured_uri: 'must be an HTTPS/SSL URI.'
|
||||||
|
|
||||||
|
doorkeeper:
|
||||||
|
applications:
|
||||||
|
confirmations:
|
||||||
|
destroy: 'Are you sure?'
|
||||||
|
buttons:
|
||||||
|
edit: 'Edit'
|
||||||
|
destroy: 'Destroy'
|
||||||
|
submit: 'Submit'
|
||||||
|
cancel: 'Cancel'
|
||||||
|
authorize: 'Authorize'
|
||||||
|
form:
|
||||||
|
error: 'Whoops! Check your form for possible errors'
|
||||||
|
help:
|
||||||
|
redirect_uri: 'Use one line per URI'
|
||||||
|
native_redirect_uri: 'Use %{native_redirect_uri} for local tests'
|
||||||
|
scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'
|
||||||
|
edit:
|
||||||
|
title: 'Edit application'
|
||||||
|
index:
|
||||||
|
title: 'Your applications'
|
||||||
|
new: 'New Application'
|
||||||
|
name: 'Name'
|
||||||
|
callback_url: 'Callback URL'
|
||||||
|
new:
|
||||||
|
title: 'New Application'
|
||||||
|
show:
|
||||||
|
title: 'Application: %{name}'
|
||||||
|
application_id: 'Application Id'
|
||||||
|
secret: 'Secret'
|
||||||
|
scopes: 'Scopes'
|
||||||
|
callback_urls: 'Callback urls'
|
||||||
|
actions: 'Actions'
|
||||||
|
|
||||||
|
authorizations:
|
||||||
|
buttons:
|
||||||
|
authorize: 'Authorize'
|
||||||
|
deny: 'Deny'
|
||||||
|
error:
|
||||||
|
title: 'An error has occurred'
|
||||||
|
new:
|
||||||
|
title: 'Authorization required'
|
||||||
|
prompt: 'Authorize %{client_name} to use your account?'
|
||||||
|
able_to: 'This application will be able to'
|
||||||
|
show:
|
||||||
|
title: 'Authorization code'
|
||||||
|
|
||||||
|
authorized_applications:
|
||||||
|
confirmations:
|
||||||
|
revoke: 'Are you sure?'
|
||||||
|
buttons:
|
||||||
|
revoke: 'Revoke'
|
||||||
|
index:
|
||||||
|
title: 'Your authorized applications'
|
||||||
|
application: 'Application'
|
||||||
|
created_at: 'Created At'
|
||||||
|
date_format: '%Y-%m-%d %H:%M:%S'
|
||||||
|
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
# Common error messages
|
||||||
|
invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
|
||||||
|
invalid_redirect_uri: 'The redirect uri included is not valid.'
|
||||||
|
unauthorized_client: 'The client is not authorized to perform this request using this method.'
|
||||||
|
access_denied: 'The resource owner or authorization server denied the request.'
|
||||||
|
invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
|
||||||
|
server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
|
||||||
|
temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
|
||||||
|
|
||||||
|
#configuration error messages
|
||||||
|
credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
|
||||||
|
resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'
|
||||||
|
|
||||||
|
# Access grant errors
|
||||||
|
unsupported_response_type: 'The authorization server does not support this response type.'
|
||||||
|
|
||||||
|
# Access token errors
|
||||||
|
invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
|
||||||
|
invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
|
||||||
|
unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
|
||||||
|
|
||||||
|
# Password Access token errors
|
||||||
|
invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found'
|
||||||
|
|
||||||
|
invalid_token:
|
||||||
|
revoked: "The access token was revoked"
|
||||||
|
expired: "The access token expired"
|
||||||
|
unknown: "The access token is invalid"
|
||||||
|
|
||||||
|
flash:
|
||||||
|
applications:
|
||||||
|
create:
|
||||||
|
notice: 'Application created.'
|
||||||
|
destroy:
|
||||||
|
notice: 'Application deleted.'
|
||||||
|
update:
|
||||||
|
notice: 'Application updated.'
|
||||||
|
authorized_applications:
|
||||||
|
destroy:
|
||||||
|
notice: 'Application revoked.'
|
||||||
|
|
||||||
|
layouts:
|
||||||
|
admin:
|
||||||
|
nav:
|
||||||
|
oauth2_provider: 'OAuth2 Provider'
|
||||||
|
applications: 'Applications'
|
||||||
|
application:
|
||||||
|
title: 'OAuth authorization required'
|
|
@ -1,4 +1,6 @@
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
use_doorkeeper
|
||||||
|
|
||||||
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
|
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
|
||||||
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
|
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
|
||||||
|
|
||||||
|
@ -9,24 +11,37 @@ Rails.application.routes.draw do
|
||||||
}
|
}
|
||||||
|
|
||||||
resources :accounts, path: 'users', only: [:show], param: :username do
|
resources :accounts, path: 'users', only: [:show], param: :username do
|
||||||
member do
|
resources :stream_entries, path: 'updates', only: [:show]
|
||||||
post :follow
|
end
|
||||||
post :unfollow
|
|
||||||
end
|
|
||||||
|
|
||||||
resources :stream_entries, path: 'updates', only: [:show] do
|
namespace :api do
|
||||||
|
# PubSubHubbub
|
||||||
|
resources :subscriptions, only: [:show]
|
||||||
|
post '/subscriptions/:id', to: 'subscriptions#update'
|
||||||
|
|
||||||
|
# Salmon
|
||||||
|
post '/salmon/:id', to: 'salmon#update', as: :salmon
|
||||||
|
|
||||||
|
# JSON / REST API
|
||||||
|
resources :statuses, only: [:create, :show] do
|
||||||
member do
|
member do
|
||||||
post :reblog
|
post :reblog
|
||||||
post :favourite
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :follows, only: [:create]
|
||||||
|
|
||||||
|
resources :accounts, only: [:show] do
|
||||||
|
member do
|
||||||
|
get :statuses
|
||||||
|
get :followers
|
||||||
|
get :following
|
||||||
|
|
||||||
|
post :follow
|
||||||
|
post :unfollow
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace :api do
|
|
||||||
resources :subscriptions, only: [:show]
|
|
||||||
post '/subscriptions/:id', to: 'subscriptions#update'
|
|
||||||
post '/salmon/:id', to: 'salmon#update', as: :salmon
|
|
||||||
end
|
|
||||||
|
|
||||||
root 'home#index'
|
root 'home#index'
|
||||||
end
|
end
|
||||||
|
|
50
db/migrate/20160306172223_create_doorkeeper_tables.rb
Normal file
50
db/migrate/20160306172223_create_doorkeeper_tables.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
class CreateDoorkeeperTables < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :oauth_applications do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
t.string :uid, null: false
|
||||||
|
t.string :secret, null: false
|
||||||
|
t.text :redirect_uri, null: false
|
||||||
|
t.string :scopes, null: false, default: ''
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :oauth_applications, :uid, unique: true
|
||||||
|
|
||||||
|
create_table :oauth_access_grants do |t|
|
||||||
|
t.integer :user_id, null: false
|
||||||
|
t.integer :application_id, null: false
|
||||||
|
t.string :token, null: false
|
||||||
|
t.integer :expires_in, null: false
|
||||||
|
t.text :redirect_uri, null: false
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.datetime :revoked_at
|
||||||
|
t.string :scopes
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :oauth_access_grants, :token, unique: true
|
||||||
|
|
||||||
|
create_table :oauth_access_tokens do |t|
|
||||||
|
t.integer :resource_owner_id
|
||||||
|
t.integer :application_id
|
||||||
|
|
||||||
|
# If you use a custom token generator you may need to change this column
|
||||||
|
# from string to text, so that it accepts tokens larger than 255
|
||||||
|
# characters. More info on custom token generators in:
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator
|
||||||
|
#
|
||||||
|
# t.text :token, null: false
|
||||||
|
t.string :token, null: false
|
||||||
|
|
||||||
|
t.string :refresh_token
|
||||||
|
t.integer :expires_in
|
||||||
|
t.datetime :revoked_at
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.string :scopes
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :oauth_access_tokens, :token, unique: true
|
||||||
|
add_index :oauth_access_tokens, :resource_owner_id
|
||||||
|
add_index :oauth_access_tokens, :refresh_token, unique: true
|
||||||
|
end
|
||||||
|
end
|
42
db/schema.rb
42
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20160305115639) do
|
ActiveRecord::Schema.define(version: 20160306172223) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -67,6 +67,46 @@ ActiveRecord::Schema.define(version: 20160305115639) do
|
||||||
|
|
||||||
add_index "mentions", ["account_id", "status_id"], name: "index_mentions_on_account_id_and_status_id", unique: true, using: :btree
|
add_index "mentions", ["account_id", "status_id"], name: "index_mentions_on_account_id_and_status_id", unique: true, using: :btree
|
||||||
|
|
||||||
|
create_table "oauth_access_grants", force: :cascade do |t|
|
||||||
|
t.integer "user_id", null: false
|
||||||
|
t.integer "application_id", null: false
|
||||||
|
t.string "token", null: false
|
||||||
|
t.integer "expires_in", null: false
|
||||||
|
t.text "redirect_uri", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "revoked_at"
|
||||||
|
t.string "scopes"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
|
||||||
|
|
||||||
|
create_table "oauth_access_tokens", force: :cascade do |t|
|
||||||
|
t.integer "resource_owner_id"
|
||||||
|
t.integer "application_id"
|
||||||
|
t.string "token", null: false
|
||||||
|
t.string "refresh_token"
|
||||||
|
t.integer "expires_in"
|
||||||
|
t.datetime "revoked_at"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.string "scopes"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
|
||||||
|
add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
|
||||||
|
add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
|
||||||
|
|
||||||
|
create_table "oauth_applications", force: :cascade do |t|
|
||||||
|
t.string "name", null: false
|
||||||
|
t.string "uid", null: false
|
||||||
|
t.string "secret", null: false
|
||||||
|
t.text "redirect_uri", null: false
|
||||||
|
t.string "scopes", default: "", null: false
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
|
||||||
|
|
||||||
create_table "statuses", force: :cascade do |t|
|
create_table "statuses", force: :cascade do |t|
|
||||||
t.string "uri"
|
t.string "uri"
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
|
|
5
spec/controllers/api/accounts_controller_spec.rb
Normal file
5
spec/controllers/api/accounts_controller_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Api::AccountsController, type: :controller do
|
||||||
|
|
||||||
|
end
|
5
spec/controllers/api/follows_controller_spec.rb
Normal file
5
spec/controllers/api/follows_controller_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Api::FollowsController, type: :controller do
|
||||||
|
|
||||||
|
end
|
5
spec/controllers/api/statuses_controller_spec.rb
Normal file
5
spec/controllers/api/statuses_controller_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Api::StatusesController, type: :controller do
|
||||||
|
|
||||||
|
end
|
15
spec/helpers/api/accounts_helper_spec.rb
Normal file
15
spec/helpers/api/accounts_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the Api::AccountsHelper. For example:
|
||||||
|
#
|
||||||
|
# describe Api::AccountsHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe Api::AccountsHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
15
spec/helpers/api/follows_helper_spec.rb
Normal file
15
spec/helpers/api/follows_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the Api::FollowsHelper. For example:
|
||||||
|
#
|
||||||
|
# describe Api::FollowsHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe Api::FollowsHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
15
spec/helpers/api/statuses_helper_spec.rb
Normal file
15
spec/helpers/api/statuses_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the Api::StatusesHelper. For example:
|
||||||
|
#
|
||||||
|
# describe Api::StatusesHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe Api::StatusesHelper, type: :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
Loading…
Reference in a new issue