fix(mcp): привязать OAuth-согласие и завершать чтение логов при обслуживании
This commit is contained in:
1 parent
78b268cc39
commit
6569dbfc60
6 files changed
+173
-8
No files matched your search
@@ -31,6 +31,65 @@ async function setup(page: Page, permissions: string[]) {
|
||||
return { mutations }
|
||||
}
|
||||
|
||||
test('synthetic: bind OAuth consent to the displayed request and submit only once', async ({ page }) => {
|
||||
await setup(page, ['mm:mcp:read'])
|
||||
const firstId = 'fa58fa40-4bcb-4616-9316-cb69463b3151'
|
||||
const secondId = 'fa58fa40-4bcb-4616-9316-cb69463b3152'
|
||||
const headers = { 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET,POST,OPTIONS', 'access-control-allow-headers': 'Authorization,Content-Type' }
|
||||
const first = { id: firstId, name: 'OAuth A', redirect_uri: 'https://a.example.test/callback', scope: 'mcp' }
|
||||
const second = { id: secondId, name: 'OAuth B', redirect_uri: 'https://b.example.test/callback', scope: 'mcp' }
|
||||
let releaseSecond!: () => void
|
||||
const secondPending = new Promise<void>(resolve => { releaseSecond = resolve })
|
||||
await page.route('**/api/mcp/oauth/requests/*', async route => {
|
||||
if (route.request().method() === 'OPTIONS') return route.fulfill({ status: 204, headers })
|
||||
const isSecond = route.request().url().endsWith(secondId)
|
||||
if (isSecond) await secondPending
|
||||
await route.fulfill({ headers, json: isSecond ? second : first })
|
||||
})
|
||||
let releasePost!: () => void
|
||||
const postPending = new Promise<void>(resolve => { releasePost = resolve })
|
||||
const decisions: unknown[] = []
|
||||
await page.route('**/api/mcp/oauth/consent', async route => {
|
||||
if (route.request().method() === 'OPTIONS') return route.fulfill({ status: 204, headers })
|
||||
decisions.push(route.request().postDataJSON())
|
||||
await postPending
|
||||
await route.fulfill({ status: 409, headers, json: { message: 'Синтетический отказ' } })
|
||||
})
|
||||
|
||||
await page.goto('/mcp?oauthRequest=' + firstId)
|
||||
const connection = page.getByLabel('Подключение', { exact: true })
|
||||
const approve = page.getByRole('button', { name: 'Разрешить', exact: true })
|
||||
const deny = page.getByRole('button', { name: 'Отклонить', exact: true })
|
||||
await expect(page.getByText(first.redirect_uri, { exact: false })).toBeVisible()
|
||||
await connection.selectOption(id)
|
||||
await expect(approve).toBeEnabled()
|
||||
const secondRequest = page.waitForRequest(request => request.method() === 'GET' && request.url().endsWith('/oauth/requests/' + secondId))
|
||||
await page.evaluate(id => window.history.pushState(null, '', '/mcp?oauthRequest=' + id), secondId)
|
||||
await secondRequest
|
||||
await expect(page.getByText(first.redirect_uri, { exact: false })).toHaveCount(0)
|
||||
await expect(connection).toHaveValue('')
|
||||
await expect(approve).toBeDisabled()
|
||||
await expect(deny).toBeDisabled()
|
||||
expect(decisions).toEqual([])
|
||||
|
||||
releaseSecond()
|
||||
await expect(page.getByText(second.redirect_uri, { exact: false })).toBeVisible()
|
||||
await connection.selectOption(id)
|
||||
await expect(approve).toBeEnabled()
|
||||
await approve.evaluate((button, denyLabel) => {
|
||||
const approve = button as HTMLButtonElement
|
||||
const deny = [...document.querySelectorAll('button')].find(node => node.textContent === denyLabel)
|
||||
approve.click()
|
||||
deny?.click()
|
||||
}, 'Отклонить')
|
||||
await expect.poll(() => decisions).toEqual([{ requestId: secondId, connectionId: id, approve: true }])
|
||||
await expect(approve).toBeDisabled()
|
||||
await expect(deny).toBeDisabled()
|
||||
releasePost()
|
||||
await expect(approve).toBeEnabled()
|
||||
expect(decisions).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('synthetic: issue only selected server/read permission and hide one-time token', async ({ page }) => {
|
||||
const state = await setup(page, ['mm:mcp:admin', 'mm:servers:read'])
|
||||
await page.goto('/mcp?tab=connections')
|
||||
|
||||
Reference in new issue
Block a user