From 3d0a113b2a52e04a5747f7b62b09d5a489c5a532 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Fri, 3 Feb 2023 17:05:12 +0800 Subject: [PATCH 1/3] feat(remote_judger/uoj): submit use own account --- remote_judger/src/providers/uoj.ts | 36 +++++++++++++++++++++++++++-- web/app/models/UOJRemoteProblem.php | 2 +- web/js/uoj.js | 32 +++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/remote_judger/src/providers/uoj.ts b/remote_judger/src/providers/uoj.ts index a7ae179..635a9d3 100644 --- a/remote_judger/src/providers/uoj.ts +++ b/remote_judger/src/providers/uoj.ts @@ -100,45 +100,60 @@ export default class UOJProvider implements IBasicProvider { } static constructFromAccountData(data) { - throw new Error('Method not implemented.'); + return new this({ + type: 'uoj', + cookie: Object.entries(data).map(([key, value]) => `${key}=${value}`), + }); } cookie: string[] = []; csrf: string; get(url: string) { - logger.debug('get', url); + logger.debug('get', url, this.cookie); + if (!url.includes('//')) url = `${this.account.endpoint || 'https://uoj.ac'}${url}`; + const req = superagent .get(url) .set('Cookie', this.cookie) .set('User-Agent', USER_AGENT); + if (this.account.proxy) return req.proxy(this.account.proxy); + return req; } post(url: string) { logger.debug('post', url, this.cookie); + if (!url.includes('//')) url = `${this.account.endpoint || 'https://uoj.ac'}${url}`; + const req = superagent .post(url) .set('Cookie', this.cookie) .set('User-Agent', USER_AGENT) .type('form'); + if (this.account.proxy) return req.proxy(this.account.proxy); + return req; } async getCsrfToken(url: string) { const { text: html, header } = await this.get(url); + if (header['set-cookie']) { this.cookie = header['set-cookie']; } + let value = /_token *: *"(.+?)"/g.exec(html); if (value) return value[1]; + value = /_token" value="(.+?)"/g.exec(html); + return value?.[1]; } @@ -150,7 +165,11 @@ export default class UOJProvider implements IBasicProvider { async ensureLogin() { if (await this.loggedIn) return true; + + if (!this.account.handle) return false; + logger.info('retry login'); + const _token = await this.getCsrfToken('/login'); const { header, text } = await this.post('/login').send({ _token, @@ -159,11 +178,14 @@ export default class UOJProvider implements IBasicProvider { // NOTE: you should pass a pre-hashed key! password: this.account.password, }); + if (header['set-cookie'] && this.cookie.length === 1) { header['set-cookie'].push(...this.cookie); this.cookie = header['set-cookie']; } + if (text === 'ok') return true; + return text; } @@ -175,6 +197,16 @@ export default class UOJProvider implements IBasicProvider { next, end ) { + if (!(await this.ensureLogin())) { + await end({ + error: true, + status: 'Judgment Failed', + message: 'Login failed', + }); + + return null; + } + const programType = langs_map[lang] || langs_map['C++']; const comment = programType.comment; diff --git a/web/app/models/UOJRemoteProblem.php b/web/app/models/UOJRemoteProblem.php index deed5ee..6dddf23 100644 --- a/web/app/models/UOJRemoteProblem.php +++ b/web/app/models/UOJRemoteProblem.php @@ -35,7 +35,7 @@ class UOJRemoteProblem { '未找到该页面', ], 'languages' => ['C', 'C++03', 'C++11', 'C++', 'C++17', 'C++20', 'Python3', 'Python2.7', 'Java8', 'Java11', 'Java17', 'Pascal'], - 'submit_type' => ['bot'], + 'submit_type' => ['bot', 'my'], ], 'loj' => [ 'name' => 'LibreOJ', diff --git a/web/js/uoj.js b/web/js/uoj.js index 47ddeae..5806726 100644 --- a/web/js/uoj.js +++ b/web/js/uoj.js @@ -1067,6 +1067,38 @@ $.fn.remote_submit_type_group = function(oj, pid, url, submit_type) { .append($('
').append(input_codeforces_jsessionid)) .append($('
').append($('
').append('请填入 Cookie 中的 JSESSIONID。'))) ).append(input_my_account_data); + } else if (oj == 'uoj') { + var uoj_account_data = {"UOJSESSID": ""}; + var input_uoj_uojsessid = $(''); + + if ('localStorage' in window) { + try { + var uoj_account_data_str = localStorage.getItem('uoj_remote_judge_uoj_account_data'); + if (uoj_account_data_str) { + uoj_account_data = JSON.parse(uoj_account_data_str); + } + } catch (e) {} + + var save_uoj_account_data = function() { + localStorage.setItem('uoj_remote_judge_uoj_account_data', JSON.stringify(uoj_account_data)); + } + } + + input_uoj_uojsessid.change(function() { + uoj_account_data.UOJSESSID = $(this).val(); + input_my_account_data.val(JSON.stringify(uoj_account_data)); + save_uoj_account_data(); + }); + + input_my_account_data.val(JSON.stringify(uoj_account_data)); + input_uoj_uojsessid.val(uoj_account_data.UOJSESSID); + + div_submit_type_my.append( + $('
') + .append($('
').append('')) + .append($('
').append(input_uoj_uojsessid)) + .append($('
').append($('
').append('请填入 Cookie 中的 UOJSESSID。'))) + ).append(input_my_account_data); } $(this).append( From 020fe9dd7a0be39448746e8c19e2a76615f2eb81 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Fri, 3 Feb 2023 17:51:18 +0800 Subject: [PATCH 2/3] chore(problem/remote): use monospace font for cookie input --- web/js/uoj.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/js/uoj.js b/web/js/uoj.js index 5806726..9146f3d 100644 --- a/web/js/uoj.js +++ b/web/js/uoj.js @@ -1069,7 +1069,7 @@ $.fn.remote_submit_type_group = function(oj, pid, url, submit_type) { ).append(input_my_account_data); } else if (oj == 'uoj') { var uoj_account_data = {"UOJSESSID": ""}; - var input_uoj_uojsessid = $(''); + var input_uoj_uojsessid = $(''); if ('localStorage' in window) { try { From b78c4d9cb56e18b555594d90971aa4e4173381a1 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Fri, 3 Feb 2023 19:55:06 +0800 Subject: [PATCH 3/3] feat(problem/remote/uoj): validate remote account status --- .../remote_judge/custom_account_validator.php | 23 +++++++++++++++++++ web/js/uoj.js | 19 +++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php b/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php index f655b20..87a3aa5 100644 --- a/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php +++ b/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php @@ -77,6 +77,29 @@ if ($type == 'luogu') { return true; } + return false; + }, 3); +} else if ($type == 'uoj') { + $curl->setFollowLocation(); + $curl->setCookie('UOJSESSID', UOJRequest::post('UOJSESSID', 'is_string', '')); + + retry_loop(function () use (&$curl, &$res) { + $curl->get(UOJRemoteProblem::$providers['uoj']['url'] . '/login'); + + if ($curl->error) { + return false; + } + + if (str_starts_with($curl->responseHeaders['Content-Type'], 'text/html')) { + if (str_contains($curl->response, '登录')) { + return false; + } + + $res = true; + + return true; + } + return false; }, 3); } else { diff --git a/web/js/uoj.js b/web/js/uoj.js index e08d151..69063ce 100644 --- a/web/js/uoj.js +++ b/web/js/uoj.js @@ -1181,16 +1181,31 @@ $.fn.remote_submit_type_group = function(oj, pid, url, submit_type) { uoj_account_data.UOJSESSID = $(this).val(); input_my_account_data.val(JSON.stringify(uoj_account_data)); save_uoj_account_data(); + my_account_validation_status.html('<span class="text-secondary">待验证</span>'); + }); + + my_account_validation_btn.click(function() { + validate_my_account({ + type: 'uoj', + UOJSESSID: input_uoj_uojsessid.val(), + }); }); input_my_account_data.val(JSON.stringify(uoj_account_data)); input_uoj_uojsessid.val(uoj_account_data.UOJSESSID); + if (uoj_account_data.UOJSESSID) { + validate_my_account({ + type: 'uoj', + UOJSESSID: uoj_account_data.UOJSESSID, + }); + } + div_submit_type_my.append( - $('<div class="row mt-3" />') + $('<div class="row mt-3 align-items-center" />') .append($('<div class="col-sm-2" />').append('<label for="input-uoj_uojsessid" class="col-form-label">UOJSESSID</label>')) .append($('<div class="col-sm-4" />').append(input_uoj_uojsessid)) - .append($('<div class="col-sm-6" />').append($('<div class="form-text" />').append('请填入 Cookie 中的 <code>UOJSESSID</code>。'))) + .append($('<div class="col-sm-6" />').append($('<div class="form-text mt-0" />').append('请填入 Cookie 中的 <code>UOJSESSID</code>。'))) ).append(input_my_account_data); }