Bind web UI access tokens to sessions (#3940)

* Add overview of active sessions

* Better display of browser/platform name

* Improve how browser information is stored and displayed for sessions overview

* Fix test

* Fix #2347 - Bind web UI access token to session

When you logout, session also destroys the access token, so it's no longer
valid. If access token is destroyed some other way, the session is also
destroyed, requiring a re-login.

Fix #1681 - Add scheduler to remove revoked access tokens and grants

* Fix test
This commit is contained in:
Eugen Rochko 2017-06-25 23:51:32 +02:00 committed by GitHub
parent 436ce03772
commit ed7dc1704d
7 changed files with 63 additions and 22 deletions

View file

@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
include UserTrackingConcern
helper_method :current_account
helper_method :current_session
helper_method :single_user_mode?
rescue_from ActionController::RoutingError, with: :not_found
@ -68,6 +69,10 @@ class ApplicationController < ActionController::Base
@current_account ||= current_user.try(:account)
end
def current_session
@current_session ||= SessionActivation.find_by(session_id: session['auth_id'])
end
def cache_collection(raw, klass)
return raw unless klass.respond_to?(:with_includes)

View file

@ -5,7 +5,7 @@ class HomeController < ApplicationController
def index
@body_classes = 'app-body'
@token = find_or_create_access_token.token
@token = current_session.token
@web_settings = Web::Setting.find_by(user: current_user)&.data || {}
@admin = Account.find_local(Setting.site_contact_username)
@streaming_api_base_url = Rails.configuration.x.streaming_api_base_url
@ -16,14 +16,4 @@ class HomeController < ApplicationController
def authenticate_user!
redirect_to(single_user_mode? ? account_path(Account.first) : about_path) unless user_signed_in?
end
def find_or_create_access_token
Doorkeeper::AccessToken.find_or_create_for(
Doorkeeper::Application.where(superapp: true).first,
current_user.id,
Doorkeeper::OAuth::Scopes.from_string('read write follow'),
Doorkeeper.configuration.access_token_expires_in,
Doorkeeper.configuration.refresh_token_enabled?
)
end
end

View file

@ -3,16 +3,23 @@
#
# Table name: session_activations
#
# id :integer not null, primary key
# user_id :integer not null
# session_id :string not null
# created_at :datetime not null
# updated_at :datetime not null
# user_agent :string default(""), not null
# ip :inet
# id :integer not null, primary key
# user_id :integer not null
# session_id :string not null
# created_at :datetime not null
# updated_at :datetime not null
# user_agent :string default(""), not null
# ip :inet
# access_token_id :integer
#
class SessionActivation < ApplicationRecord
belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', dependent: :destroy
delegate :token,
to: :access_token,
allow_nil: true
def detection
@detection ||= Browser.new(user_agent)
end
@ -25,9 +32,8 @@ class SessionActivation < ApplicationRecord
detection.platform.id
end
before_save do
self.user_agent = '' if user_agent.nil?
end
before_create :assign_access_token
before_save :assign_user_agent
class << self
def active?(id)
@ -53,4 +59,22 @@ class SessionActivation < ApplicationRecord
where('session_id != ?', id).destroy_all
end
end
private
def assign_user_agent
self.user_agent = '' if user_agent.nil?
end
def assign_access_token
superapp = Doorkeeper::Application.find_by(superapp: true)
return if superapp.nil?
self.access_token = Doorkeeper::AccessToken.create!(application_id: superapp.id,
resource_owner_id: user_id,
scopes: 'read write follow',
expires_in: Doorkeeper.configuration.access_token_expires_in,
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?)
end
end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
require 'sidekiq-scheduler'
class Scheduler::DoorkeeperCleanupScheduler
include Sidekiq::Worker
def perform
Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
end
end

View file

@ -15,3 +15,6 @@
feed_cleanup_scheduler:
cron: '0 0 * * *'
class: Scheduler::FeedCleanupScheduler
doorkeeper_cleanup_scheduler:
cron: '1 1 * * 0'
class: Scheduler::DoorkeeperCleanupScheduler

View file

@ -0,0 +1,6 @@
class AddAccessTokenIdToSessionActivations < ActiveRecord::Migration[5.1]
def change
add_column :session_activations, :access_token_id, :integer
add_foreign_key :session_activations, :oauth_access_tokens, column: :access_token_id, on_delete: :cascade
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170624134742) do
ActiveRecord::Schema.define(version: 20170625140443) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -257,6 +257,7 @@ ActiveRecord::Schema.define(version: 20170624134742) do
t.datetime "updated_at", null: false
t.string "user_agent", default: "", null: false
t.inet "ip"
t.integer "access_token_id"
t.index ["session_id"], name: "index_session_activations_on_session_id", unique: true
t.index ["user_id"], name: "index_session_activations_on_user_id"
end
@ -406,6 +407,7 @@ ActiveRecord::Schema.define(version: 20170624134742) do
add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", on_delete: :nullify
add_foreign_key "reports", "accounts", column: "target_account_id", on_delete: :cascade
add_foreign_key "reports", "accounts", on_delete: :cascade
add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade
add_foreign_key "session_activations", "users", on_delete: :cascade
add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", on_delete: :nullify
add_foreign_key "statuses", "accounts", on_delete: :cascade