token.rb
144 lines
| 4.4 KiB
| text/x-ruby
|
RubyLexer
|
r2678 | # Redmine - project management software | ||
|
r14856 | # Copyright (C) 2006-2016 Jean-Philippe Lang | ||
|
r10 | # | ||
# This program is free software; you can redistribute it and/or | ||||
# modify it under the terms of the GNU General Public License | ||||
# as published by the Free Software Foundation; either version 2 | ||||
# of the License, or (at your option) any later version. | ||||
|
r6388 | # | ||
|
r10 | # This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
|
r6388 | # | ||
|
r10 | # You should have received a copy of the GNU General Public License | ||
# along with this program; if not, write to the Free Software | ||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
class Token < ActiveRecord::Base | ||||
belongs_to :user | ||||
|
r2642 | validates_uniqueness_of :value | ||
|
r13100 | attr_protected :id | ||
|
r6388 | |||
|
r7308 | before_create :delete_previous_tokens, :generate_new_token | ||
|
r6388 | |||
|
r13300 | cattr_accessor :validity_time | ||
self.validity_time = 1.day | ||||
|
r6388 | |||
|
r15792 | class << self | ||
attr_reader :actions | ||||
def add_action(name, options) | ||||
options.assert_valid_keys(:max_instances, :validity_time) | ||||
@actions ||= {} | ||||
@actions[name.to_s] = options | ||||
end | ||||
end | ||||
add_action :api, max_instances: 1, validity_time: nil | ||||
add_action :autologin, max_instances: 10, validity_time: Proc.new { Setting.autologin.to_i.days } | ||||
add_action :feeds, max_instances: 1, validity_time: nil | ||||
add_action :recovery, max_instances: 1, validity_time: Proc.new { Token.validity_time } | ||||
add_action :register, max_instances: 1, validity_time: Proc.new { Token.validity_time } | ||||
add_action :session, max_instances: 10, validity_time: nil | ||||
|
r7308 | def generate_new_token | ||
|
r10 | self.value = Token.generate_token_value | ||
end | ||||
|
r6388 | # Return true if token has expired | ||
|
r10 | def expired? | ||
|
r15793 | validity_time = self.class.invalid_when_created_before(action) | ||
validity_time.present? && created_on < validity_time | ||||
|
r15792 | end | ||
def max_instances | ||||
Token.actions.has_key?(action) ? Token.actions[action][:max_instances] : 1 | ||||
end | ||||
def self.invalid_when_created_before(action = nil) | ||||
if Token.actions.has_key?(action) | ||||
validity_time = Token.actions[action][:validity_time] | ||||
validity_time = validity_time.call(action) if validity_time.respond_to? :call | ||||
else | ||||
validity_time = self.validity_time | ||||
end | ||||
|
r15793 | if validity_time | ||
|
r15792 | Time.now - validity_time | ||
end | ||||
|
r10 | end | ||
|
r6388 | |||
|
r10 | # Delete all expired tokens | ||
def self.destroy_expired | ||||
|
r15792 | t = Token.arel_table | ||
# Unknown actions have default validity_time | ||||
condition = t[:action].not_in(self.actions.keys).and(t[:created_on].lt(invalid_when_created_before)) | ||||
self.actions.each do |action, options| | ||||
validity_time = invalid_when_created_before(action) | ||||
# Do not delete tokens, which don't become invalid | ||||
next if validity_time.nil? | ||||
condition = condition.or( | ||||
t[:action].eq(action).and(t[:created_on].lt(validity_time)) | ||||
) | ||||
end | ||||
Token.where(condition).delete_all | ||||
|
r10 | end | ||
|
r6388 | |||
|
r11066 | # Returns the active user who owns the key for the given action | ||
def self.find_active_user(action, key, validity_days=nil) | ||||
|
r11144 | user = find_user(action, key, validity_days) | ||
if user && user.active? | ||||
user | ||||
end | ||||
end | ||||
# Returns the user who owns the key for the given action | ||||
def self.find_user(action, key, validity_days=nil) | ||||
token = find_token(action, key, validity_days) | ||||
if token | ||||
token.user | ||||
end | ||||
end | ||||
# Returns the token for action and key with an optional | ||||
# validity duration (in number of days) | ||||
def self.find_token(action, key, validity_days=nil) | ||||
|
r11066 | action = action.to_s | ||
key = key.to_s | ||||
|
r11144 | return nil unless action.present? && key =~ /\A[a-z0-9]+\z/i | ||
|
r11066 | |||
|
r11144 | token = Token.where(:action => action, :value => key).first | ||
if token && (token.action == action) && (token.value == key) && token.user | ||||
|
r11067 | if validity_days.nil? || (token.created_on > validity_days.days.ago) | ||
|
r11144 | token | ||
|
r11066 | end | ||
end | ||||
end | ||||
|
r10 | def self.generate_token_value | ||
|
r8951 | Redmine::Utils.random_hex(20) | ||
|
r10 | end | ||
|
r6388 | |||
|
r11066 | private | ||
|
r2678 | # Removes obsolete tokens (same user and action) | ||
def delete_previous_tokens | ||||
if user | ||||
|
r14353 | scope = Token.where(:user_id => user.id, :action => action) | ||
|
r15792 | if max_instances > 1 | ||
ids = scope.order(:updated_on => :desc).offset(max_instances - 1).ids | ||||
|
r14353 | if ids.any? | ||
Token.delete(ids) | ||||
end | ||||
else | ||||
scope.delete_all | ||||
end | ||||
|
r2678 | end | ||
end | ||||
|
r10 | end | ||