authDesktopAuthorization.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { createPinia, setActivePinia } from 'pinia'
  2. import { beforeEach, expect, it, vi } from 'vitest'
  3. const mocks = vi.hoisted(() => ({
  4. login: vi.fn(),
  5. setup: vi.fn(),
  6. authorizeComputer: vi.fn(),
  7. bootstrap: vi.fn(),
  8. logout: vi.fn(),
  9. changeCredentials: vi.fn(),
  10. forgetLogin: vi.fn(),
  11. }))
  12. vi.mock('@/api/auth', () => ({
  13. login: mocks.login,
  14. setup: mocks.setup,
  15. bootstrap: mocks.bootstrap,
  16. logout: mocks.logout,
  17. fetchProfiles: vi.fn(),
  18. fetchProfileIdentities: vi.fn(),
  19. changeCredentials: mocks.changeCredentials,
  20. updateAccountAvatar: vi.fn(),
  21. }))
  22. vi.mock('@/api/serverIdentity', () => ({ fetchServerIdentity: vi.fn(), onServerIdentity: () => () => {} }))
  23. vi.mock('@/api/client', () => ({
  24. ApiError: class ApiError extends Error { status = 500 },
  25. clearApiSecurityContext: vi.fn(),
  26. onApiUnauthorized: () => () => {},
  27. }))
  28. import { useAuthStore } from '@/stores/auth'
  29. const response = (role: 'admin' | 'user') => ({
  30. authRequired: true,
  31. profiles: [],
  32. csrfToken: 'fresh-csrf',
  33. user: { id: 'user-1', username: '用户', role },
  34. })
  35. beforeEach(() => {
  36. setActivePinia(createPinia())
  37. mocks.login.mockReset()
  38. mocks.setup.mockReset()
  39. mocks.authorizeComputer.mockReset()
  40. mocks.bootstrap.mockReset()
  41. mocks.logout.mockReset()
  42. mocks.changeCredentials.mockReset()
  43. mocks.forgetLogin.mockReset()
  44. Object.defineProperty(window, 'yaoyaoDesktop', { configurable: true, value: { authorizeComputer: mocks.authorizeComputer, forgetLogin: mocks.forgetLogin } })
  45. })
  46. it('re-authorizes the computer only after an explicit administrator login', async () => {
  47. mocks.login.mockResolvedValue(response('admin'))
  48. mocks.authorizeComputer.mockResolvedValue({ registered: true, hostId: 'host-1' })
  49. const auth = useAuthStore()
  50. await auth.login({ username: 'admin', password: 'secret' })
  51. expect(mocks.authorizeComputer).toHaveBeenCalledWith('fresh-csrf')
  52. expect(auth.status).toBe('authenticated')
  53. })
  54. it('does not grant computer control to a non-administrator login', async () => {
  55. mocks.login.mockResolvedValue(response('user'))
  56. const auth = useAuthStore()
  57. await auth.login({ username: 'member', password: 'secret' })
  58. expect(mocks.authorizeComputer).not.toHaveBeenCalled()
  59. expect(auth.status).toBe('authenticated')
  60. })
  61. it('keeps the administrator signed in when computer authorization fails', async () => {
  62. mocks.login.mockResolvedValue(response('admin'))
  63. mocks.authorizeComputer.mockRejectedValue(new Error('computer offline'))
  64. const auth = useAuthStore()
  65. await expect(auth.login({ username: 'admin', password: 'secret' })).resolves.toBeUndefined()
  66. expect(auth.status).toBe('authenticated')
  67. })
  68. it('forgets native authorization on explicit logout even when the server is offline', async () => {
  69. mocks.login.mockResolvedValue(response('user'))
  70. const auth = useAuthStore()
  71. await auth.login({ username: 'member', password: 'secret' })
  72. mocks.logout.mockRejectedValue(new Error('offline'))
  73. mocks.bootstrap.mockRejectedValue(new Error('offline'))
  74. await auth.logout()
  75. expect(mocks.forgetLogin).toHaveBeenCalledOnce()
  76. expect(auth.status).toBe('anonymous')
  77. expect(auth.user).toBeUndefined()
  78. })
  79. it('returns a remote desktop to login after changing its password', async () => {
  80. mocks.login.mockResolvedValue(response('user'))
  81. const auth = useAuthStore()
  82. await auth.login({ username: 'member', password: 'secret' })
  83. mocks.changeCredentials.mockResolvedValue(response('user').user)
  84. mocks.bootstrap.mockResolvedValue({ authRequired: true, profiles: [], csrfToken: 'new-csrf' })
  85. await auth.changeCredentials({ currentPassword: 'secret', newPassword: 'new-secret' })
  86. expect(mocks.forgetLogin).toHaveBeenCalledOnce()
  87. expect(auth.status).toBe('anonymous')
  88. })
  89. it('keeps the native local administrator active after setting a remote password', async () => {
  90. const local = { ...response('admin'), user: { ...response('admin').user, localDesktop: true } }
  91. mocks.bootstrap.mockResolvedValue(local)
  92. const auth = useAuthStore()
  93. await auth.bootstrap()
  94. mocks.changeCredentials.mockResolvedValue(local.user)
  95. await auth.changeCredentials({ currentPassword: '', newPassword: 'remote-password' })
  96. expect(mocks.forgetLogin).not.toHaveBeenCalled()
  97. expect(auth.status).toBe('authenticated')
  98. expect(auth.user?.localDesktop).toBe(true)
  99. })