OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / repo-request-writer-auth.test.js

80 lines · javascript ref:
// pear-git #3 — AUTO-ADMISSION auth gate for repo-request-writer.
//
// A peer that re-joined a tracker via `repo-resync` (fresh corestore) asks the
// OWNER to admit its new local key. This must grant NO new trust: it re-admits
// an ALREADY-authorized member's rotated key. The gate is `requireWriter`
// (same write-ACL as a git push), and the role is FORCED to non-indexer 'writer'
// so a self-request can never escalate to indexer/owner. These tests lock that.
import test from 'brittle'
import { repoHandlers } from '../lib/handlers/repos.js'

function fakeSvc ({ perms = [] } = {}) {
  const added = []
  const svc = {
    identityRegistry: {
      // requireWriter → getUserByIdentity(pk).globalPermissions
      getUserByIdentity: async () => ({ globalPermissions: perms })
    },
    issueRegistry: {
      getForRepo: async (name) => ({
        addWriter: async (userId, opts) => { added.push({ name, userId, ...opts }) }
      })
    }
  }
  return { svc, added }
}

const handler = (svc) => repoHandlers(svc).find((h) => h.name === 'repo-request-writer')

test('rejects an UNAUTHORIZED requester (no write perm) — no writer added', async (t) => {
  const { svc, added } = fakeSvc({ perms: [] })
  const h = handler(svc)
  await t.exception(
    h.fn({ transport: 'swarm-rpc', identity: { publicKey: 'unauth' } }, { name: 'r', writerKey: 'k1' }),
    /writer permission required/
  )
  t.is(added.length, 0, 'unauthorized self-request admits nothing')
})

test('admits an AUTHORIZED requester as a plain writer (userId = verified identity)', async (t) => {
  const { svc, added } = fakeSvc({ perms: ['write'] })
  const res = await handler(svc).fn(
    { transport: 'swarm-rpc', identity: { publicKey: 'authid' } },
    { name: 'ollama', writerKey: '0d6b5b4d' }
  )
  t.is(res.admitted, true, 'admitted')
  t.is(res.role, 'writer', 'role = writer')
  t.is(added.length, 1)
  t.is(added[0].writerKey, '0d6b5b4d', 'the requested key was added')
  t.is(added[0].role, 'writer', 'admitted as plain writer')
  t.is(added[0].userId, 'authid', 'userId is the verified requester, not caller-supplied')
})

test('NEVER auto-grants indexer/owner even if the caller asks for role:owner', async (t) => {
  const { svc, added } = fakeSvc({ perms: ['write'] })
  await handler(svc).fn(
    { transport: 'swarm-rpc', identity: { publicKey: 'authid' } },
    { name: 'ollama', writerKey: 'k2', role: 'owner' }
  )
  t.is(added[0].role, 'writer', 'role forced to writer — no self-escalation to indexer/owner')
})

test('rejects a requester with NO verified identity (unsigned)', async (t) => {
  const { svc, added } = fakeSvc({ perms: ['write'] })
  await t.exception(
    handler(svc).fn({ transport: 'swarm-rpc' }, { name: 'r', writerKey: 'k' }),
    /authentication required/
  )
  t.is(added.length, 0)
})

test('admin perm also admits (admin ⊇ write)', async (t) => {
  const { svc, added } = fakeSvc({ perms: ['admin'] })
  const res = await handler(svc).fn(
    { transport: 'swarm-rpc', identity: { publicKey: 'adminid' } },
    { name: 'ollama', writerKey: 'k3' }
  )
  t.is(res.admitted, true)
  t.is(added[0].role, 'writer')
})