token.rb 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Token < ApplicationModel
  3. include Token::TriggersSubscriptions
  4. before_create :generate_token
  5. belongs_to :user, optional: true
  6. store :preferences
  7. scope :without_sensitive_columns, -> { select(column_names - %w[persistent token]) }
  8. =begin
  9. create new token
  10. token = Token.create(action: 'PasswordReset', user_id: user.id)
  11. returns
  12. the token
  13. create new persistent token
  14. token = Token.create(
  15. action: 'api',
  16. persistent: true,
  17. user_id: user.id,
  18. preferences: {
  19. permission: {
  20. 'user_preferences.calendar' => true,
  21. }
  22. }
  23. )
  24. in case if you use it via an controller, e. g. you can verify via "curl -H "Authorization: Token token=my_token" http://...
  25. returns
  26. the token
  27. =end
  28. =begin
  29. check token
  30. user = Token.check(action: 'PasswordReset', token: '123abc12qweads')
  31. check api token with permissions
  32. user = Token.check(action: 'api', token: '123abc12qweads', permission: 'admin.session')
  33. user = Token.check(action: 'api', token: '123abc12qweads', permission: ['admin.session', 'ticket.agent'])
  34. returns
  35. user for who this token was created
  36. =end
  37. def self.check(action:, token:, permission: nil, inactive_user: false)
  38. # fetch token
  39. token = Token.find_by(action:, token:)
  40. return if !token
  41. token.user if token.check?(permission:, inactive_user:)
  42. end
  43. # Check token instance validity
  44. # Invalid non-persistant instance is removed
  45. #
  46. # @param data [Hash] check options
  47. # @option data [Boolean] :inactive_user skip checking if referenced user is active
  48. # @option data [String, Array<String>] :permission check if token has given permissions
  49. #
  50. # @return [Boolean]
  51. def check?(permission: nil, inactive_user: false)
  52. if !persistent && created_at < 1.day.ago
  53. destroy
  54. return false
  55. end
  56. # persistent token not valid if user is inactive
  57. return false if !inactive_user && persistent && user.active == false
  58. # add permission check
  59. return false if permission && !permissions?(permission)
  60. true
  61. end
  62. =begin
  63. cleanup old token
  64. Token.cleanup
  65. =end
  66. def self.cleanup
  67. Token.where(persistent: false, created_at: ...30.days.ago).delete_all
  68. true
  69. end
  70. def permissions
  71. Permission.where(
  72. name: Array(preferences[:permission]),
  73. active: true,
  74. )
  75. end
  76. def permissions?(permissions)
  77. permissions!(permissions)
  78. true
  79. rescue Exceptions::Forbidden
  80. false
  81. end
  82. def permissions!(auth_query)
  83. return true if effective_user.permissions?(auth_query) && Auth::RequestCache.permissions?(self, auth_query)
  84. raise Exceptions::Forbidden, __('Not authorized (token)!')
  85. end
  86. # allows to evaluate token permissions in context of given user instead of owner
  87. # @param [User] user to use as context for the given block
  88. # @param block to evaluate in given context
  89. def with_context(user:, &block)
  90. @effective_user = user
  91. instance_eval(&block) if block
  92. ensure
  93. @effective_user = nil
  94. end
  95. # fetch token for a user with a given action
  96. # checks token validity
  97. #
  98. # @param [String] action name
  99. # @param [Integer, User] user
  100. #
  101. # @return [Token, nil]
  102. def self.fetch(action, user_id = UserInfo.current_user_id)
  103. token = find_by(action: action, user_id: user_id)
  104. token if token&.check?
  105. end
  106. # creates or returns existing token
  107. #
  108. # @param [String] action name
  109. # @param [Integer, User] user
  110. #
  111. # @return [String]
  112. def self.ensure_token!(action, user_id = UserInfo.current_user_id, persistent: false)
  113. instance = fetch(action, user_id)
  114. return instance.token if instance.present?
  115. create!(action: action, user_id: user_id, persistent: persistent).token
  116. end
  117. # regenerates an existing token
  118. #
  119. # @param [String] action name
  120. # @param [Integer, User] user
  121. #
  122. # @return [String]
  123. def self.renew_token!(action, user_id = UserInfo.current_user_id, persistent: false)
  124. instance = fetch(action, user_id)
  125. return create(action: action, user_id: user_id, persistent: persistent).token if !instance
  126. instance.renew_token!
  127. end
  128. # regenerates an existing token
  129. #
  130. # @return [String]
  131. def renew_token!
  132. generate_token
  133. save!
  134. token
  135. end
  136. def visible_in_frontend?
  137. action == 'api' && persistent
  138. end
  139. private
  140. def generate_token
  141. loop do
  142. self.token = SecureRandom.urlsafe_base64(48)
  143. break if !Token.exists?(token: token)
  144. end
  145. true
  146. end
  147. # token owner or user set by #with_context
  148. def effective_user
  149. @effective_user || user
  150. end
  151. end