token.rb 4.3 KB

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