123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class Token < ApplicationModel
- include Token::Permissions
- include Token::TriggersSubscriptions
- before_create :generate_token
- belongs_to :user, optional: true
- store :preferences
- scope :without_sensitive_columns, -> { select(column_names - %w[persistent token]) }
- =begin
- create new token
- token = Token.create(action: 'PasswordReset', user_id: user.id)
- returns
- the token
- create new persistent token
- token = Token.create(
- action: 'api',
- persistent: true,
- user_id: user.id,
- preferences: {
- permission: {
- 'user_preferences.calendar' => true,
- }
- }
- )
- in case if you use it via an controller, e. g. you can verify via "curl -H "Authorization: Token token=…" http://…
- returns
- the token
- =end
- =begin
- check token
- user = Token.check(action: 'PasswordReset', token: '123abc12qweads')
- check api token with permissions
- user = Token.check(action: 'api', token: '123abc12qweads', permission: 'admin.session')
- user = Token.check(action: 'api', token: '123abc12qweads', permission: ['admin.session', 'ticket.agent'])
- returns
- user for who this token was created
- =end
- def self.check(action:, token:, permission: nil, inactive_user: false)
- # fetch token
- token = Token.find_by(action:, token:)
- return if !token
- token.user if token.check?(permission:, inactive_user:)
- end
- # Check token instance validity
- # Invalid non-persistant instance is removed
- #
- # @param data [Hash] check options
- # @option data [Boolean] :inactive_user skip checking if referenced user is active
- # @option data [String, Array<String>] :permission check if token has given permissions
- #
- # @return [Boolean]
- def check?(permission: nil, inactive_user: false)
- if !persistent && created_at < 1.day.ago
- destroy
- return false
- end
- # persistent token not valid if user is inactive
- return false if !inactive_user && persistent && user.active == false
- # add permission check
- return false if permission && !permissions?(permission)
- true
- end
- =begin
- cleanup old token
- Token.cleanup
- =end
- def self.cleanup
- Token.where(persistent: false, created_at: ...30.days.ago).delete_all
- true
- end
- # allows to evaluate token permissions in context of given user instead of owner
- # @param [User] user to use as context for the given block
- # @param block to evaluate in given context
- def with_context(user:, &block)
- @effective_user = user
- instance_eval(&block) if block
- ensure
- @effective_user = nil
- end
- # fetch token for a user with a given action
- # checks token validity
- #
- # @param [String] action name
- # @param [Integer, User] user
- #
- # @return [Token, nil]
- def self.fetch(action, user_id = UserInfo.current_user_id)
- token = find_by(action: action, user_id: user_id)
- token if token&.check?
- end
- # creates or returns existing token
- #
- # @param [String] action name
- # @param [Integer, User] user
- #
- # @return [String]
- def self.ensure_token!(action, user_id = UserInfo.current_user_id, persistent: false)
- instance = fetch(action, user_id)
- return instance.token if instance.present?
- create!(action: action, user_id: user_id, persistent: persistent).token
- end
- # regenerates an existing token
- #
- # @param [String] action name
- # @param [Integer, User] user
- #
- # @return [String]
- def self.renew_token!(action, user_id = UserInfo.current_user_id, persistent: false)
- instance = fetch(action, user_id)
- return create(action: action, user_id: user_id, persistent: persistent).token if !instance
- instance.renew_token!
- end
- # regenerates an existing token
- #
- # @return [String]
- def renew_token!
- generate_token
- save!
- token
- end
- def visible_in_frontend?
- action == 'api' && persistent
- end
- private
- def generate_token
- loop do
- self.token = SecureRandom.urlsafe_base64(48)
- break if !Token.exists?(token: token)
- end
- true
- end
- # token owner or user set by #with_context
- def effective_user
- @effective_user || user
- end
- end
|