token_spec.rb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Token, type: :model do
  4. subject(:token) { create(:password_reset_token, user: user) }
  5. let(:user) { create(:user) }
  6. describe '.check' do
  7. context 'with name and action matching existing token' do
  8. it 'returns the token’s user' do
  9. expect(described_class.check(action: token.action, token: token.token)).to eq(token.user)
  10. end
  11. end
  12. context 'with invalid name' do
  13. it 'returns nil' do
  14. expect(described_class.check(action: token.action, token: '1NV4L1D')).to be_nil
  15. end
  16. end
  17. context 'with invalid action' do
  18. it 'returns nil' do
  19. expect(described_class.check(action: 'PasswordReset_NotExisting', token: token.token)).to be_nil
  20. end
  21. end
  22. describe 'persistence handling' do
  23. context 'for persistent token' do
  24. subject(:token) { create(:ical_token, persistent: true, created_at: created_at) }
  25. context 'at any time' do
  26. let(:created_at) { 1.month.ago }
  27. it 'returns the token’s user' do
  28. expect(described_class.check(action: token.action, token: token.token)).to eq(token.user)
  29. end
  30. it 'does not delete the token' do
  31. token # create token
  32. expect { described_class.check(action: token.action, token: token.token) }
  33. .not_to change(described_class, :count)
  34. end
  35. end
  36. end
  37. context 'for non-persistent token' do
  38. subject(:token) { create(:password_reset_token, persistent: false, created_at: created_at) }
  39. context 'less than one day after creation' do
  40. let(:created_at) { 1.day.ago + 5 }
  41. it 'returns the token’s user' do
  42. expect(described_class.check(action: token.action, token: token.token)).to eq(token.user)
  43. end
  44. it 'does not delete the token' do
  45. token # create token
  46. expect { described_class.check(action: token.action, token: token.token) }
  47. .not_to change(described_class, :count)
  48. end
  49. end
  50. context 'at least one day after creation' do
  51. let(:created_at) { 1.day.ago }
  52. it 'returns nil' do
  53. expect(described_class.check(action: token.action, token: token.token)).to be_nil
  54. end
  55. it 'deletes the token' do
  56. token # create token
  57. expect { described_class.check(action: token.action, token: token.token) }
  58. .to change(described_class, :count).by(-1)
  59. end
  60. end
  61. end
  62. end
  63. describe 'permission matching' do
  64. subject(:token) { create(:api_token, user: agent, preferences: preferences) }
  65. let(:agent) { create(:agent) }
  66. let(:preferences) { { permission: %w[admin ticket.agent] } } # agent has no access to admin.*
  67. context 'with a permission shared by both token.user and token.preferences' do
  68. it 'returns token.user' do
  69. expect(described_class.check(action: token.action, token: token.token, permission: 'ticket.agent')).to eq(agent)
  70. end
  71. end
  72. context 'with the child of a permission shared by both token.user and token.preferences' do
  73. it 'returns token.user' do
  74. expect(described_class.check(action: token.action, token: token.token, permission: 'ticket.agent.foo')).to eq(agent)
  75. end
  76. end
  77. context 'with the parent of a permission shared by both token.user and token.preferences' do
  78. it 'returns nil' do
  79. expect(described_class.check(action: token.action, token: token.token, permission: 'ticket')).to be_nil
  80. end
  81. end
  82. context 'with a permission in token.preferences, but not on token.user' do
  83. it 'returns nil' do
  84. expect(described_class.check(action: token.action, token: token.token, permission: 'admin')).to be_nil
  85. end
  86. end
  87. context 'with a permission not in token.preferences, but on token.user' do
  88. it 'returns nil' do
  89. expect(described_class.check(action: token.action, token: token.token, permission: 'cti.agent')).to be_nil
  90. end
  91. end
  92. context 'with non-existent permission' do
  93. it 'returns nil' do
  94. expect(described_class.check(action: token.action, token: token.token, permission: 'foo')).to be_nil
  95. end
  96. end
  97. context 'with multiple permissions, where at least one is shared by both token.user and token.preferences' do
  98. it 'returns token.user' do
  99. expect(described_class.check(action: token.action, token: token.token, permission: %w[foo ticket.agent])).to eq(agent)
  100. end
  101. end
  102. end
  103. end
  104. describe '#permissions?' do
  105. subject(:token) do
  106. create(:token, user: user, preferences: { permission: [permission_name] })
  107. end
  108. let(:user) { create(:user, roles: [role]) }
  109. let(:role) { create(:role, permissions: [permission]) }
  110. let(:permission) { create(:permission, name: permission_name) }
  111. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  112. let(:permission_name) { 'foo' }
  113. context 'when given that exact permission' do
  114. it 'returns true' do
  115. expect(token.permissions?('foo')).to be(true)
  116. end
  117. end
  118. context 'when given a sub-permission (i.e., child permission)' do
  119. let(:subpermission) { create(:permission, name: 'foo.bar') }
  120. context 'that exists' do
  121. before { subpermission }
  122. it 'returns true' do
  123. expect(token.permissions?('foo.bar')).to be(true)
  124. end
  125. end
  126. context 'that is inactive' do
  127. before { subpermission.update(active: false) }
  128. it 'returns false' do
  129. expect(token.permissions?('foo.bar')).to be(false)
  130. end
  131. end
  132. context 'that does not exist' do
  133. it 'returns true' do
  134. expect(token.permissions?('foo.bar')).to be(true)
  135. end
  136. end
  137. end
  138. context 'when given a glob' do
  139. context 'matching that permission' do
  140. it 'returns true' do
  141. expect(token.permissions?('foo.*')).to be(true)
  142. end
  143. end
  144. context 'NOT matching that permission' do
  145. it 'returns false' do
  146. expect(token.permissions?('bar.*')).to be(false)
  147. end
  148. end
  149. end
  150. end
  151. context 'with privileges for a sub-permission (e.g., "foo.bar", not "foo")' do
  152. let(:permission_name) { 'foo.bar' }
  153. context 'when given that exact sub-permission' do
  154. it 'returns true' do
  155. expect(token.permissions?('foo.bar')).to be(true)
  156. end
  157. context 'but the permission is inactive' do
  158. before { permission.update(active: false) }
  159. it 'returns false' do
  160. expect(token.permissions?('foo.bar')).to be(false)
  161. end
  162. end
  163. end
  164. context 'when given a sibling sub-permission' do
  165. let(:sibling_permission) { create(:permission, name: 'foo.baz') }
  166. context 'that exists' do
  167. before { sibling_permission }
  168. it 'returns false' do
  169. expect(token.permissions?('foo.baz')).to be(false)
  170. end
  171. end
  172. context 'that does not exist' do
  173. it 'returns false' do
  174. expect(token.permissions?('foo.baz')).to be(false)
  175. end
  176. end
  177. end
  178. context 'when given the parent permission' do
  179. it 'returns false' do
  180. expect(token.permissions?('foo')).to be(false)
  181. end
  182. end
  183. context 'when given a glob' do
  184. context 'matching that sub-permission' do
  185. it 'returns true' do
  186. expect(token.permissions?('foo.*')).to be(true)
  187. end
  188. context 'but the permission is inactive' do
  189. before { permission.update(active: false) }
  190. context 'and user.permissions?(...) doesn’t fail' do
  191. let(:role) { create(:role, permissions: [parent_permission]) }
  192. let(:parent_permission) { create(:permission, name: permission_name.split('.').first) }
  193. it 'returns false' do
  194. expect(token.permissions?('foo.*')).to be(false)
  195. end
  196. end
  197. end
  198. end
  199. context 'NOT matching that sub-permission' do
  200. it 'returns false' do
  201. expect(token.permissions?('bar.*')).to be(false)
  202. end
  203. end
  204. end
  205. end
  206. end
  207. describe '#fetch' do
  208. it 'returns nil when not present' do
  209. expect(described_class.fetch('token', user)).to be_nil
  210. end
  211. it 'returns token when present' do
  212. token
  213. expect(described_class.fetch(token.action, token.user.id)).to eq(token)
  214. end
  215. end
  216. describe '.ensure_token!' do
  217. it 'returns token when not present' do
  218. expect(described_class.ensure_token!('token', user.id)).to be_present
  219. end
  220. it 'returns token when present' do
  221. token
  222. expect(described_class.ensure_token!(token.action, token.user.id)).to eq(token.token)
  223. end
  224. describe 'with persistent argument' do
  225. it 'creates not-persistent token if argument omitted' do
  226. described_class.ensure_token!('token', user.id)
  227. expect(described_class.find_by(action: 'token')).not_to be_persistent
  228. end
  229. it 'creates persistent token when flag given' do
  230. described_class.ensure_token!('token', user.id, persistent: true)
  231. expect(described_class.find_by(action: 'token')).to be_persistent
  232. end
  233. end
  234. end
  235. describe '#renew_token!' do
  236. it 'changes token' do
  237. expect { token.renew_token! }.to change { token.reload.token }
  238. end
  239. end
  240. describe '.renew_token!' do
  241. it 'creates token when not present' do
  242. expect(described_class.renew_token!('token', user.id)).to be_present
  243. end
  244. it 'returns token when present' do
  245. token
  246. expect { described_class.renew_token!(token.action, token.user.id) }.to change { token.reload.token }
  247. end
  248. describe 'with persistent argument' do
  249. it 'creates not-persistent token if argument omitted' do
  250. described_class.renew_token!('token', user.id)
  251. expect(described_class.find_by(action: 'token')).not_to be_persistent
  252. end
  253. it 'creates persistent token when flag given' do
  254. described_class.renew_token!('token', user.id, persistent: true)
  255. expect(described_class.find_by(action: 'token')).to be_persistent
  256. end
  257. end
  258. end
  259. end