##// END OF EJS Templates
Don't compare timestamps with 0, triggers SQL errors with PostgreSQL (#10840)....
Jean-Philippe Lang -
r15793:82d96258f441
parent child
Show More
@@ -1,145 +1,144
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Token < ActiveRecord::Base
18 class Token < ActiveRecord::Base
19 belongs_to :user
19 belongs_to :user
20 validates_uniqueness_of :value
20 validates_uniqueness_of :value
21 attr_protected :id
21 attr_protected :id
22
22
23 before_create :delete_previous_tokens, :generate_new_token
23 before_create :delete_previous_tokens, :generate_new_token
24
24
25 cattr_accessor :validity_time
25 cattr_accessor :validity_time
26 self.validity_time = 1.day
26 self.validity_time = 1.day
27
27
28 class << self
28 class << self
29 attr_reader :actions
29 attr_reader :actions
30
30
31 def add_action(name, options)
31 def add_action(name, options)
32 options.assert_valid_keys(:max_instances, :validity_time)
32 options.assert_valid_keys(:max_instances, :validity_time)
33 @actions ||= {}
33 @actions ||= {}
34 @actions[name.to_s] = options
34 @actions[name.to_s] = options
35 end
35 end
36 end
36 end
37
37
38 add_action :api, max_instances: 1, validity_time: nil
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 }
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
40 add_action :feeds, max_instances: 1, validity_time: nil
41 add_action :recovery, max_instances: 1, validity_time: Proc.new { Token.validity_time }
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 }
42 add_action :register, max_instances: 1, validity_time: Proc.new { Token.validity_time }
43 add_action :session, max_instances: 10, validity_time: nil
43 add_action :session, max_instances: 10, validity_time: nil
44
44
45 def generate_new_token
45 def generate_new_token
46 self.value = Token.generate_token_value
46 self.value = Token.generate_token_value
47 end
47 end
48
48
49 # Return true if token has expired
49 # Return true if token has expired
50 def expired?
50 def expired?
51 return created_on < self.class.invalid_when_created_before(action)
51 validity_time = self.class.invalid_when_created_before(action)
52 validity_time.present? && created_on < validity_time
52 end
53 end
53
54
54 def max_instances
55 def max_instances
55 Token.actions.has_key?(action) ? Token.actions[action][:max_instances] : 1
56 Token.actions.has_key?(action) ? Token.actions[action][:max_instances] : 1
56 end
57 end
57
58
58 def self.invalid_when_created_before(action = nil)
59 def self.invalid_when_created_before(action = nil)
59 if Token.actions.has_key?(action)
60 if Token.actions.has_key?(action)
60 validity_time = Token.actions[action][:validity_time]
61 validity_time = Token.actions[action][:validity_time]
61 validity_time = validity_time.call(action) if validity_time.respond_to? :call
62 validity_time = validity_time.call(action) if validity_time.respond_to? :call
62 else
63 else
63 validity_time = self.validity_time
64 validity_time = self.validity_time
64 end
65 end
65
66
66 if validity_time.nil?
67 if validity_time
67 0
68 else
69 Time.now - validity_time
68 Time.now - validity_time
70 end
69 end
71 end
70 end
72
71
73 # Delete all expired tokens
72 # Delete all expired tokens
74 def self.destroy_expired
73 def self.destroy_expired
75 t = Token.arel_table
74 t = Token.arel_table
76
75
77 # Unknown actions have default validity_time
76 # Unknown actions have default validity_time
78 condition = t[:action].not_in(self.actions.keys).and(t[:created_on].lt(invalid_when_created_before))
77 condition = t[:action].not_in(self.actions.keys).and(t[:created_on].lt(invalid_when_created_before))
79
78
80 self.actions.each do |action, options|
79 self.actions.each do |action, options|
81 validity_time = invalid_when_created_before(action)
80 validity_time = invalid_when_created_before(action)
82
81
83 # Do not delete tokens, which don't become invalid
82 # Do not delete tokens, which don't become invalid
84 next if validity_time.nil?
83 next if validity_time.nil?
85
84
86 condition = condition.or(
85 condition = condition.or(
87 t[:action].eq(action).and(t[:created_on].lt(validity_time))
86 t[:action].eq(action).and(t[:created_on].lt(validity_time))
88 )
87 )
89 end
88 end
90
89
91 Token.where(condition).delete_all
90 Token.where(condition).delete_all
92 end
91 end
93
92
94 # Returns the active user who owns the key for the given action
93 # Returns the active user who owns the key for the given action
95 def self.find_active_user(action, key, validity_days=nil)
94 def self.find_active_user(action, key, validity_days=nil)
96 user = find_user(action, key, validity_days)
95 user = find_user(action, key, validity_days)
97 if user && user.active?
96 if user && user.active?
98 user
97 user
99 end
98 end
100 end
99 end
101
100
102 # Returns the user who owns the key for the given action
101 # Returns the user who owns the key for the given action
103 def self.find_user(action, key, validity_days=nil)
102 def self.find_user(action, key, validity_days=nil)
104 token = find_token(action, key, validity_days)
103 token = find_token(action, key, validity_days)
105 if token
104 if token
106 token.user
105 token.user
107 end
106 end
108 end
107 end
109
108
110 # Returns the token for action and key with an optional
109 # Returns the token for action and key with an optional
111 # validity duration (in number of days)
110 # validity duration (in number of days)
112 def self.find_token(action, key, validity_days=nil)
111 def self.find_token(action, key, validity_days=nil)
113 action = action.to_s
112 action = action.to_s
114 key = key.to_s
113 key = key.to_s
115 return nil unless action.present? && key =~ /\A[a-z0-9]+\z/i
114 return nil unless action.present? && key =~ /\A[a-z0-9]+\z/i
116
115
117 token = Token.where(:action => action, :value => key).first
116 token = Token.where(:action => action, :value => key).first
118 if token && (token.action == action) && (token.value == key) && token.user
117 if token && (token.action == action) && (token.value == key) && token.user
119 if validity_days.nil? || (token.created_on > validity_days.days.ago)
118 if validity_days.nil? || (token.created_on > validity_days.days.ago)
120 token
119 token
121 end
120 end
122 end
121 end
123 end
122 end
124
123
125 def self.generate_token_value
124 def self.generate_token_value
126 Redmine::Utils.random_hex(20)
125 Redmine::Utils.random_hex(20)
127 end
126 end
128
127
129 private
128 private
130
129
131 # Removes obsolete tokens (same user and action)
130 # Removes obsolete tokens (same user and action)
132 def delete_previous_tokens
131 def delete_previous_tokens
133 if user
132 if user
134 scope = Token.where(:user_id => user.id, :action => action)
133 scope = Token.where(:user_id => user.id, :action => action)
135 if max_instances > 1
134 if max_instances > 1
136 ids = scope.order(:updated_on => :desc).offset(max_instances - 1).ids
135 ids = scope.order(:updated_on => :desc).offset(max_instances - 1).ids
137 if ids.any?
136 if ids.any?
138 Token.delete(ids)
137 Token.delete(ids)
139 end
138 end
140 else
139 else
141 scope.delete_all
140 scope.delete_all
142 end
141 end
143 end
142 end
144 end
143 end
145 end
144 end
General Comments 0
You need to be logged in to leave comments. Login now