Project

General

Profile

Feature #10840 » 0001-10840-Define-token-action-properties-explicitly.patch

refactoring of token action handling (rebased onto r16138) - Gregor Schmidt, 2017-01-05 10:08

View differences:

app/models/token.rb
25 25
  cattr_accessor :validity_time
26 26
  self.validity_time = 1.day
27 27

  
28
  class << self
29
    attr_reader :actions
30

  
31
    def add_action(name, options)
32
      options.assert_valid_keys(:max_instances, :validity_time)
33
      @actions ||= {}
34
      @actions[name.to_s] = options
35
    end
36
  end
37

  
38
  add_action :api,       max_instances: 1,  validity_time: nil
39
  add_action :autologin, max_instances: 10, validity_time: Proc.new { Setting.autologin.to_i.days }
40
  add_action :feeds,     max_instances: 1,  validity_time: nil
41
  add_action :recovery,  max_instances: 1,  validity_time: Proc.new { Token.validity_time }
42
  add_action :register,  max_instances: 1,  validity_time: Proc.new { Token.validity_time }
43
  add_action :session,   max_instances: 10, validity_time: nil
44

  
28 45
  def generate_new_token
29 46
    self.value = Token.generate_token_value
30 47
  end
31 48

  
32 49
  # Return true if token has expired
33 50
  def expired?
34
    return Time.now > self.created_on + self.class.validity_time
51
    return created_on < self.class.invalid_when_created_before(action)
52
  end
53

  
54
  def max_instances
55
    Token.actions.has_key?(action) ? Token.actions[action][:max_instances] : 1
56
  end
57

  
58
  def self.invalid_when_created_before(action = nil)
59
    if Token.actions.has_key?(action)
60
      validity_time = Token.actions[action][:validity_time]
61
      validity_time = validity_time.call(action) if validity_time.respond_to? :call
62
    else
63
      validity_time = self.validity_time
64
    end
65

  
66
    if validity_time.nil?
67
      0
68
    else
69
      Time.now - validity_time
70
    end
35 71
  end
36 72

  
37 73
  # Delete all expired tokens
38 74
  def self.destroy_expired
39
    Token.where("action NOT IN (?) AND created_on < ?", ['feeds', 'api', 'session'], Time.now - validity_time).delete_all
75
    t = Token.arel_table
76

  
77
    # Unknown actions have default validity_time
78
    condition = t[:action].not_in(self.actions.keys).and(t[:created_on].lt(invalid_when_created_before))
79

  
80
    self.actions.each do |action, options|
81
      validity_time = invalid_when_created_before(action)
82

  
83
      # Do not delete tokens, which don't become invalid
84
      next if validity_time.nil?
85

  
86
      condition = condition.or(
87
        t[:action].eq(action).and(t[:created_on].lt(validity_time))
88
      )
89
    end
90

  
91
    Token.where(condition).delete_all
40 92
  end
41 93

  
42 94
  # Returns the active user who owns the key for the given action
......
80 132
  def delete_previous_tokens
81 133
    if user
82 134
      scope = Token.where(:user_id => user.id, :action => action)
83
      if action == 'session'
84
        ids = scope.order(:updated_on => :desc).offset(9).ids
135
      if max_instances > 1
136
        ids = scope.order(:updated_on => :desc).offset(max_instances - 1).ids
85 137
        if ids.any?
86 138
          Token.delete(ids)
87 139
        end
test/unit/token_test.rb
29 29

  
30 30
  def test_create_should_remove_existing_tokens
31 31
    user = User.find(1)
32
    t1 = Token.create(:user => user, :action => 'autologin')
33
    t2 = Token.create(:user => user, :action => 'autologin')
32
    t1 = Token.create(:user => user, :action => 'register')
33
    t2 = Token.create(:user => user, :action => 'register')
34 34
    assert_not_equal t1.value, t2.value
35 35
    assert !Token.exists?(t1.id)
36 36
    assert  Token.exists?(t2.id)
37 37
  end
38 38

  
39
  def test_create_session_token_should_keep_last_10_tokens
39
  def test_create_session_or_autologin_token_should_keep_last_10_tokens
40 40
    Token.delete_all
41 41
    user = User.find(1)
42 42

  
43
    assert_difference 'Token.count', 10 do
44
      10.times { Token.create!(:user => user, :action => 'session') }
45
    end
43
    ["autologin", "session"].each do |action|
44
      assert_difference 'Token.count', 10 do
45
        10.times { Token.create!(:user => user, :action => action) }
46
      end
46 47

  
47
    assert_no_difference 'Token.count' do
48
      Token.create!(:user => user, :action => 'session')
48
      assert_no_difference 'Token.count' do
49
        Token.create!(:user => user, :action => action)
50
      end
49 51
    end
50 52
  end
51 53

  
52
  def test_destroy_expired_should_not_destroy_feeds_and_api_tokens
54
  def test_destroy_expired_should_not_destroy_session_feeds_and_api_tokens
53 55
    Token.delete_all
54 56

  
55 57
    Token.create!(:user_id => 1, :action => 'api', :created_on => 7.days.ago)
56 58
    Token.create!(:user_id => 1, :action => 'feeds', :created_on => 7.days.ago)
59
    Token.create!(:user_id => 1, :action => 'session', :created_on => 7.days.ago)
57 60

  
58 61
    assert_no_difference 'Token.count' do
59 62
      assert_equal 0, Token.destroy_expired
......
63 66
  def test_destroy_expired_should_destroy_expired_tokens
64 67
    Token.delete_all
65 68

  
66
    Token.create!(:user_id => 1, :action => 'autologin', :created_on => 7.days.ago)
67
    Token.create!(:user_id => 2, :action => 'autologin', :created_on => 3.days.ago)
68
    Token.create!(:user_id => 3, :action => 'autologin', :created_on => 1.hour.ago)
69
    # Expiration of autologin tokens is determined by Setting.autologin
70
    Setting.autologin = "7"
71
    Token.create!(:user_id => 2, :action => 'autologin', :created_on => 3.weeks.ago)
72
    Token.create!(:user_id => 3, :action => 'autologin', :created_on => 3.days.ago)
73

  
74
    # Expiration of register and recovery tokens is determined by Token.validity_time
75
    Token.create!(:user_id => 1, :action => 'register', :created_on => 7.days.ago)
76
    Token.create!(:user_id => 3, :action => 'register', :created_on => 7.hours.ago)
77

  
78
    Token.create!(:user_id => 2, :action => 'recovery', :created_on => 3.days.ago)
79
    Token.create!(:user_id => 3, :action => 'recovery', :created_on => 3.hours.ago)
80

  
81
    # Expiration of tokens with unknown action is determined by Token.validity_time
82
    Token.create!(:user_id => 2, :action => 'unknown_action', :created_on => 2.days.ago)
83
    Token.create!(:user_id => 3, :action => 'unknown_action', :created_on => 2.hours.ago)
69 84

  
70
    assert_difference 'Token.count', -2 do
71
      assert_equal 2, Token.destroy_expired
85
    assert_difference 'Token.count', -4 do
86
      assert_equal 4, Token.destroy_expired
72 87
    end
73 88
  end
74 89

  
(3-3/3)