Found in the Windows VM re-test. - A PC installed without the shared key got 401 from POST /api/endpoints/enroll while the shared key was allowed, so no request was recorded and no admin could approve it. Enroll now never refuses: a valid shared key only decides auto-issue on a clean match; no key or an invalid key creates or updates the one pending request for that hostname and serial. An approved request still issues only to its own nonce. - A PC blocked with "server certificate not trusted by this PC" could never be queued again after trust was fixed, because the blocker comes from its last failed upgrade. Naming the PC in a wave now queues it when trust is its only blocker, and the response lists it under "retried"; "next n" still skips it. The Migration table lets such a PC be selected. - counts.pending on GET /enrollment counts pending requests, including ones for unknown hostnames; counts.pendingcomputers keeps the per-computer figure.
134 lines
5.3 KiB
JavaScript
134 lines
5.3 KiB
JavaScript
/**
|
|
* Endpoints (shared key) settings page.
|
|
*
|
|
* The shared key only matters to PCs that have not enrolled, so the page must
|
|
* say how far enrollment has got, and the regenerate and revoke confirms must
|
|
* name how many PCs the change actually reaches.
|
|
*/
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
import { mount, flushPromises, RouterLinkStub } from '@vue/test-utils'
|
|
|
|
vi.mock('@/api', () => ({
|
|
default: { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() }
|
|
}))
|
|
vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }) }))
|
|
|
|
import api from '@/api'
|
|
import AgentKeySettings from './AgentKeySettings.vue'
|
|
|
|
const KEY = { tokenprefix: 'abcd', createddate: null, lastusedat: null }
|
|
|
|
function enrollmentPayload(settings = { sharedkeyallowed: true, autoenroll: true }) {
|
|
return {
|
|
data: {
|
|
data: {
|
|
settings,
|
|
counts: { enrolled: 30, notenrolled: 8, pendingcomputers: 2, pending: 3 },
|
|
computers: [],
|
|
requests: [
|
|
{ requestid: 1, status: 'pending', reason: 'serial mismatch' },
|
|
{ requestid: 2, status: 'pending', reason: 'awaiting approval' },
|
|
{ requestid: 3, status: 'pending', reason: 'reenroll blocked' },
|
|
{ requestid: 4, status: 'issued', reason: null }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async function render(payload = enrollmentPayload()) {
|
|
api.get.mockImplementation((path) => {
|
|
if (path === '/endpoints/agentkey') {
|
|
return Promise.resolve({ data: { data: { key: KEY, envkeyfallback: false } } })
|
|
}
|
|
if (path === '/endpoints/enrollment') return Promise.resolve(payload)
|
|
return Promise.resolve({ data: { data: {} } })
|
|
})
|
|
api.post.mockResolvedValue({ data: { data: { key: KEY } } })
|
|
api.delete.mockResolvedValue({ data: { data: { key: null } } })
|
|
const wrapper = mount(AgentKeySettings, { global: { stubs: { RouterLink: RouterLinkStub } } })
|
|
await flushPromises()
|
|
return wrapper
|
|
}
|
|
|
|
describe('AgentKeySettings shared key section', () => {
|
|
beforeEach(() => {
|
|
api.get.mockReset()
|
|
api.post.mockReset()
|
|
api.delete.mockReset()
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
it('titles the section for the per-PC model', async () => {
|
|
const wrapper = await render()
|
|
expect(wrapper.text()).toContain('Shared key (used until PCs enroll)')
|
|
expect(wrapper.text()).not.toContain('Key management')
|
|
expect(wrapper.text()).toMatch(/Enrolled PCs ignore it/)
|
|
expect(wrapper.text()).toMatch(/At cutover/)
|
|
})
|
|
|
|
it('shows enrollment progress from one small enrollment read', async () => {
|
|
const wrapper = await render()
|
|
expect(api.get).toHaveBeenCalledWith('/endpoints/enrollment', { params: { perpage: 1 } })
|
|
const summary = wrapper.find('.enrollment-summary').text()
|
|
expect(summary).toContain('30 of 40 PCs enrolled')
|
|
expect(summary).toContain('10 still on the shared key')
|
|
// A blocked PC asking again is not a request anyone has to decide.
|
|
expect(summary).toContain('2 pending enrollment requests')
|
|
const link = wrapper.findComponent(RouterLinkStub)
|
|
expect(link.props('to')).toEqual({ name: 'endpoints-enrollment-settings' })
|
|
})
|
|
|
|
it('regenerate confirm names the PCs that need the new key', async () => {
|
|
const confirmSpy = vi.fn(() => true)
|
|
vi.stubGlobal('confirm', confirmSpy)
|
|
const wrapper = await render()
|
|
const button = wrapper.findAll('button').find(b => b.text() === 'Regenerate key')
|
|
await button.trigger('click')
|
|
await flushPromises()
|
|
expect(confirmSpy.mock.calls[0][0]).toMatch(/10 PCs not enrolled will need the new key/)
|
|
expect(confirmSpy.mock.calls[0][0]).toMatch(/Enrolled PCs are not affected/)
|
|
expect(api.post).toHaveBeenCalledWith('/endpoints/agentkey/regenerate')
|
|
})
|
|
|
|
it('revoke confirm names the PCs that lose access', async () => {
|
|
const confirmSpy = vi.fn(() => true)
|
|
vi.stubGlobal('confirm', confirmSpy)
|
|
const wrapper = await render()
|
|
const button = wrapper.findAll('button').find(b => b.text() === 'Revoke key')
|
|
await button.trigger('click')
|
|
await flushPromises()
|
|
expect(confirmSpy.mock.calls[0][0]).toMatch(/10 PCs not enrolled will lose access/)
|
|
expect(api.delete).toHaveBeenCalledWith('/endpoints/agentkey')
|
|
})
|
|
|
|
it('sends nothing when revoke is declined', async () => {
|
|
vi.stubGlobal('confirm', vi.fn(() => false))
|
|
const wrapper = await render()
|
|
const button = wrapper.findAll('button').find(b => b.text() === 'Revoke key')
|
|
await button.trigger('click')
|
|
await flushPromises()
|
|
expect(api.delete).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('still manages the key when the enrollment read fails', async () => {
|
|
api.get.mockImplementation((path) => {
|
|
if (path === '/endpoints/enrollment') return Promise.reject(new Error('403'))
|
|
if (path === '/endpoints/agentkey') {
|
|
return Promise.resolve({ data: { data: { key: KEY, envkeyfallback: false } } })
|
|
}
|
|
return Promise.resolve({ data: { data: {} } })
|
|
})
|
|
vi.stubGlobal('confirm', vi.fn(() => true))
|
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
const wrapper = mount(AgentKeySettings, { global: { stubs: { RouterLink: RouterLinkStub } } })
|
|
await flushPromises()
|
|
expect(wrapper.find('.enrollment-summary').exists()).toBe(false)
|
|
const button = wrapper.findAll('button').find(b => b.text() === 'Revoke key')
|
|
expect(button.exists()).toBe(true)
|
|
})
|
|
})
|