Something went wrong. Try again.
AtAuth
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379/** * Database Service - Proxy Methods Tests */import { describe, it, expect, beforeEach, afterEach } from 'vitest';import crypto from 'crypto';import { DatabaseService } from './database.js';
describe('Proxy Sessions', () => { let db: DatabaseService;
beforeEach(() => { db = new DatabaseService(':memory:'); });
afterEach(() => { db.close(); });
function makeSession(overrides: Partial<{ id: string; did: string; handle: string; created_at: number; expires_at: number; last_activity: number; user_agent: string; ip_address: string; }> = {}) { const now = Math.floor(Date.now() / 1000); return { id: overrides.id || crypto.randomBytes(16).toString('base64url'), did: overrides.did || 'did:plc:test123', handle: overrides.handle || 'test.bsky.social', created_at: overrides.created_at || now, expires_at: overrides.expires_at || now + 604800, last_activity: overrides.last_activity || now, user_agent: overrides.user_agent, ip_address: overrides.ip_address, }; }
it('should create and retrieve a proxy session', () => { const session = makeSession({ ip_address: '1.2.3.4', user_agent: 'TestAgent/1.0' }); db.createProxySession(session);
const retrieved = db.getProxySession(session.id); expect(retrieved).not.toBeNull(); expect(retrieved!.id).toBe(session.id); expect(retrieved!.did).toBe(session.did); expect(retrieved!.handle).toBe(session.handle); expect(retrieved!.ip_address).toBe('1.2.3.4'); expect(retrieved!.user_agent).toBe('TestAgent/1.0'); });
it('should return null for non-existent session', () => { expect(db.getProxySession('nonexistent')).toBeNull(); });
it('should update session activity timestamp', () => { const session = makeSession(); db.createProxySession(session);
// Wait a tick so timestamp differs const before = db.getProxySession(session.id)!.last_activity; db.updateProxySessionActivity(session.id); const after = db.getProxySession(session.id)!.last_activity;
expect(after).toBeGreaterThanOrEqual(before); });
it('should delete a proxy session', () => { const session = makeSession(); db.createProxySession(session); expect(db.getProxySession(session.id)).not.toBeNull();
db.deleteProxySession(session.id); expect(db.getProxySession(session.id)).toBeNull(); });
it('should delete all sessions for a user', () => { const did = 'did:plc:userA'; db.createProxySession(makeSession({ id: 's1', did })); db.createProxySession(makeSession({ id: 's2', did })); db.createProxySession(makeSession({ id: 's3', did: 'did:plc:userB' }));
const deleted = db.deleteProxySessionsForUser(did); expect(deleted).toBe(2); expect(db.getProxySession('s1')).toBeNull(); expect(db.getProxySession('s2')).toBeNull(); expect(db.getProxySession('s3')).not.toBeNull(); });
it('should clean up expired proxy sessions', () => { const now = Math.floor(Date.now() / 1000); db.createProxySession(makeSession({ id: 'expired1', expires_at: now - 100 })); db.createProxySession(makeSession({ id: 'expired2', expires_at: now - 1 })); db.createProxySession(makeSession({ id: 'active', expires_at: now + 3600 }));
const deleted = db.cleanupExpiredProxySessions(); expect(deleted).toBe(2); expect(db.getProxySession('expired1')).toBeNull(); expect(db.getProxySession('expired2')).toBeNull(); expect(db.getProxySession('active')).not.toBeNull(); });
it('should list active proxy sessions', () => { const now = Math.floor(Date.now() / 1000); db.createProxySession(makeSession({ id: 'a1', did: 'did:plc:u1', expires_at: now + 3600 })); db.createProxySession(makeSession({ id: 'a2', did: 'did:plc:u2', expires_at: now + 3600 })); db.createProxySession(makeSession({ id: 'expired', did: 'did:plc:u1', expires_at: now - 10 }));
const all = db.getAllProxySessions(); expect(all).toHaveLength(2);
const filtered = db.getAllProxySessions('did:plc:u1'); expect(filtered).toHaveLength(1); expect(filtered[0].id).toBe('a1'); });
it('should respect limit on getAllProxySessions', () => { const now = Math.floor(Date.now() / 1000); for (let i = 0; i < 5; i++) { db.createProxySession(makeSession({ id: `s${i}`, expires_at: now + 3600 })); }
const limited = db.getAllProxySessions(undefined, 3); expect(limited).toHaveLength(3); });});
describe('Proxy Allowed Origins', () => { let db: DatabaseService;
beforeEach(() => { db = new DatabaseService(':memory:'); });
afterEach(() => { db.close(); });
it('should add and list allowed origins', () => { db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG'); db.addProxyAllowedOrigin('https://chat.example.com', 'Element');
const origins = db.listProxyAllowedOrigins(); expect(origins).toHaveLength(2); // Sorted by name ASC expect(origins[0].name).toBe('Element'); expect(origins[1].name).toBe('SearXNG'); });
it('should return created origin with id', () => { const created = db.addProxyAllowedOrigin('https://test.example.com', 'Test'); expect(created.id).toBeGreaterThan(0); expect(created.origin).toBe('https://test.example.com'); expect(created.name).toBe('Test'); expect(created.created_at).toBeGreaterThan(0); });
it('should reject duplicate origins', () => { db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG'); expect(() => { db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG Copy'); }).toThrow(/UNIQUE/); });
it('should remove an allowed origin', () => { const created = db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG'); db.removeProxyAllowedOrigin(created.id); expect(db.listProxyAllowedOrigins()).toHaveLength(0); });
it('should check if origin is allowed', () => { db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG');
expect(db.isProxyOriginAllowed('https://search.example.com')).toBe(true); expect(db.isProxyOriginAllowed('https://evil.example.com')).toBe(false); });});
describe('Proxy Auth Requests', () => { let db: DatabaseService;
beforeEach(() => { db = new DatabaseService(':memory:'); });
afterEach(() => { db.close(); });
it('should save and retrieve an auth request', () => { const now = Math.floor(Date.now() / 1000); const req = { id: 'auth-req-123', redirect_uri: 'https://search.example.com/path', created_at: now, expires_at: now + 600, }; db.saveProxyAuthRequest(req);
const retrieved = db.getProxyAuthRequest('auth-req-123'); expect(retrieved).not.toBeNull(); expect(retrieved!.redirect_uri).toBe('https://search.example.com/path'); expect(retrieved!.expires_at).toBe(now + 600); });
it('should return null for non-existent auth request', () => { expect(db.getProxyAuthRequest('nonexistent')).toBeNull(); });
it('should delete an auth request', () => { const now = Math.floor(Date.now() / 1000); db.saveProxyAuthRequest({ id: 'del-me', redirect_uri: 'https://example.com', created_at: now, expires_at: now + 600, });
db.deleteProxyAuthRequest('del-me'); expect(db.getProxyAuthRequest('del-me')).toBeNull(); });
it('should clean up expired auth requests', () => { const now = Math.floor(Date.now() / 1000); db.saveProxyAuthRequest({ id: 'expired1', redirect_uri: 'https://example.com', created_at: now - 700, expires_at: now - 100, }); db.saveProxyAuthRequest({ id: 'active', redirect_uri: 'https://example.com', created_at: now, expires_at: now + 600, });
const deleted = db.cleanupExpiredProxyAuthRequests(); expect(deleted).toBe(1); expect(db.getProxyAuthRequest('expired1')).toBeNull(); expect(db.getProxyAuthRequest('active')).not.toBeNull(); });});
describe('Proxy Access Rules', () => { let db: DatabaseService;
beforeEach(() => { db = new DatabaseService(':memory:'); });
afterEach(() => { db.close(); });
it('should create and list access rules', () => { const rule = db.createProxyAccessRule({ origin_id: null, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*.example.com', description: 'PDS users', });
expect(rule.id).toBeGreaterThan(0); expect(rule.rule_type).toBe('allow'); expect(rule.subject_value).toBe('*.example.com');
const rules = db.listProxyAccessRules(); expect(rules).toHaveLength(1); expect(rules[0].description).toBe('PDS users'); });
it('should delete an access rule', () => { const rule = db.createProxyAccessRule({ origin_id: null, rule_type: 'allow', subject_type: 'did', subject_value: 'did:plc:test123', description: null, });
db.deleteProxyAccessRule(rule.id); expect(db.listProxyAccessRules()).toHaveLength(0); });
it('should filter rules by origin_id', () => { const origin = db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG');
db.createProxyAccessRule({ origin_id: origin.id, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*', description: 'Origin rule', });
db.createProxyAccessRule({ origin_id: null, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*.example.com', description: 'Global rule', });
// Filter by origin ID should include origin-specific + global rules const filtered = db.listProxyAccessRules(origin.id); expect(filtered).toHaveLength(2);
// All rules const all = db.listProxyAccessRules(); expect(all).toHaveLength(2); });
it('should partition rules for access check', () => { const origin = db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG');
db.createProxyAccessRule({ origin_id: origin.id, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*.example.com', description: null, }); db.createProxyAccessRule({ origin_id: origin.id, rule_type: 'deny', subject_type: 'did', subject_value: 'did:plc:banned', description: null, }); db.createProxyAccessRule({ origin_id: null, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*', description: null, });
const result = db.getProxyAccessRulesForCheck(origin.id); expect(result.denyRules).toHaveLength(1); expect(result.denyRules[0].subject_value).toBe('did:plc:banned'); expect(result.originAllowRules).toHaveLength(1); expect(result.originAllowRules[0].subject_value).toBe('*.example.com'); expect(result.globalAllowRules).toHaveLength(1); expect(result.globalAllowRules[0].subject_value).toBe('*'); });
it('should cascade delete rules when origin is removed', () => { const origin = db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG');
db.createProxyAccessRule({ origin_id: origin.id, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*', description: null, });
db.createProxyAccessRule({ origin_id: null, rule_type: 'allow', subject_type: 'handle_pattern', subject_value: '*', description: 'Global survives', });
db.removeProxyAllowedOrigin(origin.id);
const rules = db.listProxyAccessRules(); expect(rules).toHaveLength(1); expect(rules[0].description).toBe('Global survives'); });
it('should look up origin ID by origin URL', () => { const origin = db.addProxyAllowedOrigin('https://search.example.com', 'SearXNG'); expect(db.getOriginIdByOrigin('https://search.example.com')).toBe(origin.id); expect(db.getOriginIdByOrigin('https://nonexistent.example.com')).toBeNull(); });});