fix: preserve explicit Doodstream web login and reuse OTP sessions
CI / verify (push) Canceled after 0s
CI / verify (push) Canceled after 0s
This commit is contained in:
@@ -49,6 +49,22 @@ function otpRequired(message = 'OTP erforderlich') {
|
||||
return error;
|
||||
}
|
||||
|
||||
test('explicit web login is not overridden by a stored API key', () => {
|
||||
assert.deepEqual(selectUploadAuth('doodstream.com', { authType: 'login', username: 'user', password: 'secret', apiKey: 'KEY' }), { username: 'user', password: 'secret' });
|
||||
});
|
||||
|
||||
test('upload reuses the confirmed OTP session without another login', async () => {
|
||||
let logins = 0;
|
||||
const uploadSession = {};
|
||||
const coordinator = createDoodstreamOtpCoordinator({
|
||||
createUploader: () => ({ async login() { logins++; }, cloneSession: () => uploadSession })
|
||||
});
|
||||
const health = await coordinator.check({ username: 'user', password: 'secret' });
|
||||
assert.equal(health.status, 'ok');
|
||||
assert.equal(await coordinator.acquire({ username: 'user', password: 'secret' }), uploadSession);
|
||||
assert.equal(logins, 1);
|
||||
});
|
||||
|
||||
test('concurrent and repeated Doodstream checks request only one OTP', async () => {
|
||||
let loginCalls = 0;
|
||||
let releaseLogin;
|
||||
@@ -87,7 +103,7 @@ test('Doodstream OTP verification reuses the challenged uploader session', async
|
||||
assert.equal((await coordinator.check({ username: 'user', password: 'secret' })).status, 'otp_required');
|
||||
assert.deepEqual(await coordinator.check({ username: 'user', password: 'secret', otp: '123456' }), {
|
||||
status: 'ok',
|
||||
message: 'Login ok, Upload-Seite bereit'
|
||||
message: 'Login erfolgreich'
|
||||
});
|
||||
assert.equal(created, 1);
|
||||
assert.deepEqual(calls, ['', '123456']);
|
||||
|
||||
@@ -246,7 +246,7 @@ test('login cannot report success when the redirected session is still a guest',
|
||||
? JSON.stringify({ status: 'redirect', message: '/dashboard' })
|
||||
: `<home-upload :upload="{ utype: 'anon', sess_id: '' }"></home-upload>`
|
||||
));
|
||||
await assert.rejects(up.login('user', 'password'), /guest=true; sessionField=true; cookies=0/);
|
||||
await assert.rejects(up.login('user', 'password'), /sess_id nicht gefunden/);
|
||||
assert.equal(up.sessId, '');
|
||||
});
|
||||
|
||||
@@ -261,6 +261,56 @@ test('login redirect chains cannot forward session cookies to another origin', a
|
||||
assert.equal(calls, 1);
|
||||
});
|
||||
|
||||
test('authenticated dashboard verifies web login without an upload token or API calls', async (t) => {
|
||||
const up = new DoodstreamUploader();
|
||||
t.mock.method(globalThis, 'fetch', async (url) => {
|
||||
const target = new URL(url);
|
||||
assert.equal(target.hostname, 'doodstream.com');
|
||||
if (target.searchParams.get('op') === 'login_ajax') {
|
||||
return new Response(JSON.stringify({ status: 'redirect', message: '/dashboard' }), {
|
||||
headers: { 'set-cookie': 'xfsts=SESSION; Path=/' }
|
||||
});
|
||||
}
|
||||
return new Response('<a href="/settings">Settings</a><a href="/videos">Videos</a><a href="/?op=logout">Logout</a>');
|
||||
});
|
||||
await up.login('user', 'secret', '123456');
|
||||
assert.equal(up.apiKey, '');
|
||||
assert.equal(up.sessId, '');
|
||||
assert.equal(up.cookies.get('xfsts'), 'SESSION');
|
||||
});
|
||||
|
||||
test('an unverified settings token cannot make a missing-session login pass', async (t) => {
|
||||
const up = new DoodstreamUploader();
|
||||
t.mock.method(globalThis, 'fetch', async url => {
|
||||
const target = new URL(url);
|
||||
if (target.hostname === 'doodapi.co') return new Response(JSON.stringify({ status: 403 }));
|
||||
if (target.searchParams.get('op') === 'login_ajax') return new Response(JSON.stringify({ status: 'success' }));
|
||||
if (target.pathname === '/settings') return new Response('<label>API Key</label><input value="INVALIDKEY12345678901234567890">');
|
||||
return new Response('');
|
||||
});
|
||||
await assert.rejects(up.login('user', 'secret'), /sess_id nicht gefunden/);
|
||||
assert.equal(up.apiKey, '');
|
||||
});
|
||||
|
||||
test('web server unavailability is a hoster failure rather than a missing login session', async () => {
|
||||
const up = new DoodstreamUploader();
|
||||
up._fetch = async () => new Response(JSON.stringify({ success: false, message: 'No servers available for uploads' }));
|
||||
await assert.rejects(up._getUploadServer(), error => error.hosterTransient === true && /No servers available/.test(error.message));
|
||||
});
|
||||
|
||||
test('parallel web uploads receive independent cookie and upload-session state', () => {
|
||||
const up = new DoodstreamUploader();
|
||||
up.cookies.set('xfsts', 'AUTH_SESSION');
|
||||
up.sessId = 'UPLOAD_SESSION';
|
||||
const first = up.cloneSession();
|
||||
const second = up.cloneSession();
|
||||
first.cookies.set('xfsts', 'CHANGED');
|
||||
first.sessId = 'CHANGED';
|
||||
assert.equal(second.cookies.get('xfsts'), 'AUTH_SESSION');
|
||||
assert.equal(second.sessId, 'UPLOAD_SESSION');
|
||||
assert.equal(up.cookies.get('xfsts'), 'AUTH_SESSION');
|
||||
});
|
||||
|
||||
test('getUploadServer: parses the current upload_get_srv response', async () => {
|
||||
const up = new DoodstreamUploader();
|
||||
up._fetch = async (url) => {
|
||||
|
||||
@@ -70,6 +70,28 @@ describe('UploadManager', () => {
|
||||
assert.ok(events.length > 0, 'should emit at least one progress event');
|
||||
});
|
||||
|
||||
it('web-login uploads use the confirmed web session without API discovery or a second login', async () => {
|
||||
let uploads = 0;
|
||||
const mgr = new UploadManager({}, {}, {}, {
|
||||
acquireDoodstreamSession: async task => {
|
||||
assert.equal(task.username, 'user');
|
||||
return {
|
||||
async login() { throw new Error('must not request another OTP'); },
|
||||
async upload(file) {
|
||||
assert.equal(file, '/test/video.mp4');
|
||||
uploads++;
|
||||
return { file_code: 'WEBFILE123', download_url: 'https://doodstream.com/d/WEBFILE123' };
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
mgr._resolveDoodstreamApiKey = async () => { throw new Error('must not change web login to API'); };
|
||||
const result = await mgr._executeUpload({ hoster: 'doodstream.com', file: '/test/video.mp4', username: 'user', password: 'secret' });
|
||||
assert.equal(result.file_code, 'WEBFILE123');
|
||||
assert.equal(uploads, 1);
|
||||
assert.equal(mockUploadFile.mock.callCount(), 0);
|
||||
});
|
||||
|
||||
it('emits job-settled after releasing job resources', async () => {
|
||||
const mgr = new UploadManager({});
|
||||
let settled;
|
||||
|
||||
Reference in New Issue
Block a user