Verify API key recovery and revocation through browser UI
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -605,3 +605,11 @@ API anahtarı istemcisinde 30 saniye zaman aşımı, yanıt biçimi kontrolü ve
|
|||||||
Doğrulama: boş başarılı liste, 503, geçersiz JSON/veri, geç gelen farklı oturum yanıtı ve zaman aşımında tek oluşturma isteği. Gerçek izole yerel HTTP sunucusunda oluşturma → gizli alanları içermeyen liste → MCP initialize erişimi → son kullanım zamanı → iptal → 401 zinciri geçti. Başka hesap iptal edemedi; iptal yanıtında limit korundu. Test anahtarları geçici veritabanında tutuldu ve çıktıya yazılmadı.
|
Doğrulama: boş başarılı liste, 503, geçersiz JSON/veri, geç gelen farklı oturum yanıtı ve zaman aşımında tek oluşturma isteği. Gerçek izole yerel HTTP sunucusunda oluşturma → gizli alanları içermeyen liste → MCP initialize erişimi → son kullanım zamanı → iptal → 401 zinciri geçti. Başka hesap iptal edemedi; iptal yanıtında limit korundu. Test anahtarları geçici veritabanında tutuldu ve çıktıya yazılmadı.
|
||||||
|
|
||||||
Sonuç: 132 test başarılı; üretim derlemesi geçti. Tarayıcıda hata/hesap değişimi/kopyalama kabulü henüz yapılmadı. Büyük paket uyarıları ve D01–D11 kalan işleri açık. Canlı yayın yapılmadı.
|
Sonuç: 132 test başarılı; üretim derlemesi geçti. Tarayıcıda hata/hesap değişimi/kopyalama kabulü henüz yapılmadı. Büyük paket uyarıları ve D01–D11 kalan işleri açık. Canlı yayın yapılmadı.
|
||||||
|
|
||||||
|
### D09 — API anahtarı tarayıcı kabulü
|
||||||
|
|
||||||
|
Yeni, izole localhost:5176 test sunucusunda görünür test kontrol ekranıyla yalnız anahtar listeleme isteğine 503 hatası uygulandı. API ekranı çökmedi; hata ve yeniden deneme göründü, oluşturma kapandı. Kontrol ekranından hata kaldırılınca aynı uygulama ekranındaki yeniden deneme 0/10 listesini getirdi. Gerçek test anahtarı oluşturuldu: tek seferlik kutu, ad ve 1/10 sayacı görüldü. İptal sonrası kutu kayboldu, liste boşaldı ve limit 0/10 olarak korundu.
|
||||||
|
|
||||||
|
Normal test hesabına görünür giriş ekranından geçildiğinde 0/0 limit, Kurumsal plan açıklaması ve kapalı oluşturma görüldü. Önceki anahtar adı görünmedi. Bu geçiş tam sayfa yüklemesiyle yapılmıştır; devam eden istek sırasında hesap değişimi bu tarayıcı kontrolünde kanıtlanmış değildir. Pano izin hatası da açık kabul maddesidir.
|
||||||
|
|
||||||
|
Kanıt: qa-api-key-lifecycle.json, sekiz kontrol doğru. Anahtar değeri ve öneki kanıtta maskelendi; oluşturulan anahtar test sonunda iptal edildi. Test yardımcısındaki hata kontrolleri yalnız yerel QA sunucusundadır, üretime dahil değildir. Üretim kodu değişmediğinden önceki 132 test/build tekrarlanmadı; QA değişikliği çalışan tarayıcı akışıyla doğrulandı. Tam plan ve canlı yayın açık.
|
||||||
|
|||||||
@@ -11,11 +11,17 @@ process.env.AUTH_SECRET=randomBytes(32).toString('hex');
|
|||||||
const {signToken}=await import('../server/auth.mjs');
|
const {signToken}=await import('../server/auth.mjs');
|
||||||
const user={email:'browser-qa@example.test',name:'Laboratuvar QA',isAdmin:false};
|
const user={email:'browser-qa@example.test',name:'Laboratuvar QA',isAdmin:false};
|
||||||
const token=signToken(user);
|
const token=signToken(user);
|
||||||
|
const keyUser={email:'keys-browser-qa@example.test',name:'API QA',isAdmin:true};
|
||||||
|
const keyToken=signToken(keyUser);
|
||||||
|
let apiKeyListFailure=false;
|
||||||
const probe=netServer();await new Promise(r=>probe.listen(0,'127.0.0.1',r));const port=probe.address().port;await new Promise(r=>probe.close(r));
|
const probe=netServer();await new Promise(r=>probe.listen(0,'127.0.0.1',r));const port=probe.address().port;await new Promise(r=>probe.close(r));
|
||||||
const api=spawn(process.execPath,['server/index.js'],{cwd:new URL('..',import.meta.url),env:{PATH:process.env.PATH,NODE_ENV:'test',AUTH_SECRET:process.env.AUTH_SECRET,IHALEIST_USER_DATA_DIR:dir,IHALEIST_SHARED_DATA_DIR:join(dir,'shared'),PORT:String(port),UYUM_GOMME:'0'},stdio:'ignore'});
|
const api=spawn(process.execPath,['server/index.js'],{cwd:new URL('..',import.meta.url),env:{PATH:process.env.PATH,NODE_ENV:'test',AUTH_SECRET:process.env.AUTH_SECRET,IHALEIST_USER_DATA_DIR:dir,IHALEIST_SHARED_DATA_DIR:join(dir,'shared'),PORT:String(port),UYUM_GOMME:'0'},stdio:'ignore'});
|
||||||
const entries=[['1582991','Giresun','10.09.2026','10:00'],['1534665','Kayseri','22.09.2026','11:00'],['1499061','Akdeniz','22.09.2026','10:30'],['1573235','Aksaray','09.09.2026','10:00'],['1523851','Tepecik','11.09.2026','10:00']];
|
const entries=[['1582991','Giresun','10.09.2026','10:00'],['1534665','Kayseri','22.09.2026','11:00'],['1499061','Akdeniz','22.09.2026','10:30'],['1573235','Aksaray','09.09.2026','10:00'],['1523851','Tepecik','11.09.2026','10:00']];
|
||||||
const tenders=entries.map(([id,city,date,time])=>({id:'qa-'+id,ikn:'2026/'+id,baslik:'QA — '+city+' laboratuvar',kurum:city+' TEST KURUMU',sehir:city,tarih:date,saat:time,alimTuru:'Mal Alımı',sektor:'Laboratuvar & Ölçüm',sektorler:['Laboratuvar & Ölçüm'],durum:'Açık',takipte:false,kanbanStatus:'incelenecek',items:[{id:'1',ad:'Sentetik test kalemi',miktar:2,birim:'adet'}],aciklama:'Bu yalnızca yerel arayüz test verisidir. Teklif geçerlilik süresi 120 takvim günü. Birim fiyat teklif. '.repeat(3)}));
|
const tenders=entries.map(([id,city,date,time])=>({id:'qa-'+id,ikn:'2026/'+id,baslik:'QA — '+city+' laboratuvar',kurum:city+' TEST KURUMU',sehir:city,tarih:date,saat:time,alimTuru:'Mal Alımı',sektor:'Laboratuvar & Ölçüm',sektorler:['Laboratuvar & Ölçüm'],durum:'Açık',takipte:false,kanbanStatus:'incelenecek',items:[{id:'1',ad:'Sentetik test kalemi',miktar:2,birim:'adet'}],aciklama:'Bu yalnızca yerel arayüz test verisidir. Teklif geçerlilik süresi 120 takvim günü. Birim fiyat teklif. '.repeat(3)}));
|
||||||
const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use((req,res,next)=>{
|
const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use((req,res,next)=>{
|
||||||
|
if(req.url==='/qa-api-controls') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel API testi</h1><p>Yalnız geçici test deposunu etkiler.</p><button id="login">API test hesabıyla gir</button><button id="fail">Liste hatasını aç</button><button id="recover">Liste hatasını kapat</button><p role="status"></p><script type="module">import {setSession} from '/src/services/auth.js';document.querySelector('#login').onclick=()=>{setSession(${JSON.stringify(keyToken)},${JSON.stringify(keyUser)});location.href='/';};for(const [id,mode] of [['fail','on'],['recover','off']])document.getElementById(id).onclick=async()=>{await fetch('/qa-api-failure?mode='+mode,{method:'POST'});document.querySelector('[role=status]').textContent=mode==='on'?'Liste hatası açık':'Liste hatası kapalı';};</script></html>`);return;}
|
||||||
|
if(req.method==='POST' && req.url?.startsWith('/qa-api-failure?')) {apiKeyListFailure=new URL(req.url,'http://localhost').searchParams.get('mode')==='on';res.end('ok');return;}
|
||||||
|
if(apiKeyListFailure && req.method==='GET' && req.url==='/api/me/apikeys') {res.writeHead(503,{'Content-Type':'application/json'});res.end(JSON.stringify({success:false,error:'QA: Anahtar deposuna erişilemiyor.'}));return;}
|
||||||
if(req.url==='/qa-signin') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel laboratuvar testi</h1><p>Sentetik ihale verileri ve geçici kullanıcı deposu kullanılır.</p><button id="login">Test hesabıyla gir</button><script type="module">import {setSession} from '/src/services/auth.js';document.querySelector('#login').onclick=()=>{setSession(${JSON.stringify(token)},${JSON.stringify(user)});location.href='/';};</script></html>`);return;}
|
if(req.url==='/qa-signin') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel laboratuvar testi</h1><p>Sentetik ihale verileri ve geçici kullanıcı deposu kullanılır.</p><button id="login">Test hesabıyla gir</button><script type="module">import {setSession} from '/src/services/auth.js';document.querySelector('#login').onclick=()=>{setSession(${JSON.stringify(token)},${JSON.stringify(user)});location.href='/';};</script></html>`);return;}
|
||||||
if(req.url?.startsWith('/api/tenders')){res.setHeader('Content-Type','application/json');res.end(JSON.stringify({success:true,tenders,totalCount:5,todayCount:0,historyCount:0,source:'Yerel sentetik QA',fetchedAt:new Date().toISOString()}));return;}
|
if(req.url?.startsWith('/api/tenders')){res.setHeader('Content-Type','application/json');res.end(JSON.stringify({success:true,tenders,totalCount:5,todayCount:0,historyCount:0,source:'Yerel sentetik QA',fetchedAt:new Date().toISOString()}));return;}
|
||||||
next();
|
next();
|
||||||
|
|||||||
Reference in New Issue
Block a user