Add ability to revoke recorder tokens

private-asciicasts
Marcin Kulik 9 years ago
parent fd03554e2e
commit e05fbd574f

@ -30,3 +30,7 @@
.actions
margin: 20px 0 0 0
.edit-page
> .row:last-child
margin-top: 30px

@ -5,10 +5,17 @@ class ApiTokensController < ApplicationController
def create
current_user.assign_api_token(params[:api_token])
redirect_to profile_path(current_user),
notice: "Successfully registered your API token. ^5"
notice: "Successfully registered your recorder token."
rescue ActiveRecord::RecordInvalid, ApiToken::ApiTokenTakenError
render :error
end
def destroy
api_token = ApiToken.find(params[:id])
authorize api_token
api_token.revoke!
redirect_to edit_user_path, notice: "Token revoked."
end
end

@ -15,18 +15,18 @@ class UsersController < ApplicationController
end
def edit
@user = current_user
authorize @user
authorize current_user
render locals: { page: UserEditPagePresenter.new(current_user) }
end
def update
@user = User.find(current_user.id)
authorize @user
authorize current_user
user = User.find(current_user.id)
if @user.update_attributes(update_params)
redirect_to profile_path(@user), notice: 'Account settings saved.'
if user.update_attributes(update_params)
redirect_to profile_path(user), notice: 'Account settings saved.'
else
render :edit, status: 422
render :edit, status: 422, locals: { page: UserEditPagePresenter.new(user) }
end
end

@ -7,6 +7,9 @@ class ApiToken < ActiveRecord::Base
validates :user, :token, presence: true
validates :token, uniqueness: true
scope :active, -> { where(revoked_at: nil) }
scope :revoked, -> { where('revoked_at IS NOT NULL') }
def self.for_token(token)
where(token: token).first
end
@ -27,6 +30,10 @@ class ApiToken < ActiveRecord::Base
user.merge_to(target_user)
end
def revoke!
update!(revoked_at: Time.now)
end
private
def taken?

@ -56,6 +56,14 @@ class User < ActiveRecord::Base
new(temporary_username: 'anonymous')
end
def active_api_tokens
api_tokens.active
end
def revoked_api_tokens
api_tokens.revoked
end
def confirmed?
email.present?
end

@ -0,0 +1,9 @@
class ApiTokenPolicy < ApplicationPolicy
def destroy?
return false unless user
user.admin? || record.user == user
end
end

@ -0,0 +1,27 @@
class UserEditPagePresenter
attr_reader :user
def initialize(user)
@user = user
end
def active_tokens
sort(user.active_api_tokens)
end
def revoked_tokens
sort(user.revoked_api_tokens)
end
def show_tokens?
!active_tokens.empty? || !revoked_tokens.empty?
end
private
def sort(tokens)
tokens.sort_by { |token| token.created_at }.reverse
end
end

@ -66,9 +66,9 @@ publishing it on asciinema.org.
## `auth`
__Assign local API token to asciinema.org account.__
__Assign local recorder token to asciinema.org account.__
On every machine you install asciinema recorder, you get a new, unique API
On every machine you install asciinema recorder, you get a new, unique recorder
token. This command connects this local token with your asciinema.org account,
and links all asciicasts recorded on this machine with the account.
@ -79,6 +79,6 @@ URL.
NOTE: it is __necessary__ to do this if you want to __edit or delete__ your
recordings on asciinema.org.
You can synchronize your `~/.asciinema/config` file (which keeps the API
token) across the machines but that's not necessary. You can assign new
You can synchronize your `~/.asciinema/config` file (which keeps the token)
across the machines but that's not necessary. You can assign new recorder
tokens to your account from as many machines as you want.

@ -1,8 +1,8 @@
.container
.container.edit-page
.row
.col-md-9
= horizontal_form_for @user do |f|
legend Your settings
= horizontal_form_for page.user do |f|
legend Account settings
= f.input :username
= f.input :email, required: true
@ -11,3 +11,40 @@
= f.buttons do
= f.button :submit, 'Save', class: 'btn-primary'
= link_to 'Cancel', profile_path(current_user), class: 'btn'
.row
.col-md-12
legend Recorder tokens
- if page.show_tokens?
p The following recorder tokens have been associated with your account:
- unless page.active_tokens.empty?
ul
- page.active_tokens.each do |token|
li
= token.token
' registered
= time_ago_tag token.created_at
' -
= link_to 'Revoke', api_token_path(token), method: :delete
- unless page.revoked_tokens.empty?
ul
- page.revoked_tokens.each do |token|
li.revoked-token
= token.token
' registered
= time_ago_tag token.created_at
' , revoked
= time_ago_tag token.revoked_at
- else
p
| If you want your recordings to be assigned to your profile
you have to register your local recorder token.
p
' There is currently no recorder token associated with your account.
Run
code asciinema auth
| in your terminal to register one.

@ -33,6 +33,7 @@ Rails.application.routes.draw do
get "/login/:token" => "sessions#create", as: :login_token
get "/logout" => "sessions#destroy"
resources :api_tokens, only: [:create, :destroy]
get "/connect/:api_token" => "api_tokens#create"
resource :user

@ -0,0 +1,5 @@
class AddRevokedAtToApiTokens < ActiveRecord::Migration
def change
add_column :api_tokens, :revoked_at, :datetime
end
end

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150327171201) do
ActiveRecord::Schema.define(version: 20150401161102) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -21,6 +21,7 @@ ActiveRecord::Schema.define(version: 20150327171201) do
t.string "token", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "revoked_at"
end
add_index "api_tokens", ["token"], name: "index_api_tokens_on_token", using: :btree

@ -0,0 +1,38 @@
require 'rails_helper'
feature "Recorder tokens management" do
let!(:user) { create(:user) }
scenario 'Listing tokens when user has none' do
login_as user
visit edit_user_path
expect(page).to have_content('asciinema auth')
end
scenario 'Listing tokens when user has some' do
api_token = create(:api_token, user: user)
login_as user
visit edit_user_path
expect(page).to have_content(api_token.token)
expect(page).to have_link('Revoke')
expect(page).to have_no_content('asciinema auth')
end
scenario 'Revoking a token' do
api_token = create(:api_token, user: user)
login_as user
visit edit_user_path
click_on "Revoke"
expect(page).to have_content(api_token.token)
expect(page).to have_no_link('Revoke')
end
end

@ -0,0 +1,27 @@
require 'rails_helper'
describe ApiTokenPolicy do
subject { described_class }
permissions :destroy? do
it "denies access if user is nil" do
expect(subject).not_to permit(nil, ApiToken.new)
end
it "grants access if user is admin" do
user = stub_model(User, admin?: true)
expect(subject).to permit(user, ApiToken.new)
end
it "grants access if user is the owner of the token" do
user = stub_model(User, admin?: false)
expect(subject).to permit(user, ApiToken.new(user: user))
end
it "denies access if user isn't the owner of the token" do
expect(subject).not_to permit(User.new, ApiToken.new(user: User.new))
end
end
end
Loading…
Cancel
Save