From b3af28dc437bbf844e9be9bb668bdfae3c80b345 Mon Sep 17 00:00:00 2001 From: undefined Date: Mon, 3 Aug 2020 20:10:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0oauth=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 1 + hydro/handler/user.ts | 108 ++-------------------------------------- hydro/model/setting.ts | 5 -- hydro/service/server.ts | 10 +++- hydro/utils.ts | 48 +++++++++++++++++- package.json | 5 +- yarn.lock | 19 +------ 7 files changed, 64 insertions(+), 132 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a8fc2a8e5..c07334458 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,6 +26,7 @@ module.exports = { 'no-bitwise': 'off', 'no-console': 'off', 'no-extend-native': 'off', + 'no-inner-declarations': 'off', 'no-multi-assign': 'off', 'no-nested-ternary': 'off', 'no-param-reassign': 'off', diff --git a/hydro/handler/user.ts b/hydro/handler/user.ts index 292e3691d..c7d93a541 100644 --- a/hydro/handler/user.ts +++ b/hydro/handler/user.ts @@ -1,4 +1,3 @@ -import superagent from 'superagent'; import moment from 'moment-timezone'; import { UserAlreadyExistError, InvalidTokenError, VerifyPasswordError, @@ -22,12 +21,6 @@ import paginate from '../lib/paginate'; class UserLoginHandler extends Handler { async get() { - const [loginWithGithub, loginWithGoogle] = await system.getMany( - ['oauth.githubappid', 'oauth.googleappid'], - ); - this.response.body = { - loginWithGithub, loginWithGoogle, - }; this.response.template = 'user_login.html'; } @@ -222,109 +215,16 @@ class UserSearchHandler extends Handler { } class OauthHandler extends Handler { - async github() { - const [appid, [state]] = await Promise.all([ - system.get('oauth.githubappid'), - token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }), - ]); - this.response.redirect = `https://github.com/login/oauth/authorize?client_id=${appid}&state=${state}`; - } - - async google() { - const [appid, url, [state]] = await Promise.all([ - system.get('oauth.googleappid'), - system.get('server.url'), - token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }), - ]); - this.response.redirect = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${appid}&response_type=code&redirect_uri=${url}oauth/google/callback&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&state=${state}`; - } - - async get({ type }) { - if (type === 'github') await this.github(); - else if (type === 'google') await this.google(); + @param('type', Types.String) + async get(domainId: string, type: string) { + if (global.Hydro.lib[`oauth_${type}`]) await global.Hydro.lib[`oauth_${type}`].get.call(this); } } class OauthCallbackHandler extends Handler { - @param('state', Types.String) - @param('code', Types.String) - async github(domainId: string, state: string, code: string) { - const [appid, secret, url, proxy, s] = await Promise.all([ - system.get('oauth.githubappid'), - system.get('oauth.githubsecret'), - system.get('server.url'), - system.get('proxy'), - token.get(state, token.TYPE_OAUTH), - ]); - const res = await superagent.post('https://github.com/login/oauth/access_token') - .proxy(proxy) - .send({ - client_id: appid, - client_secret: secret, - code, - redirect_uri: `${url}oauth/github/callback`, - state, - }) - .set('accept', 'application/json'); - if (res.body.error) { - throw new UserFacingError( - res.body.error, res.body.error_description, res.body.error_uri, - ); - } - const t = res.body.access_token; - const userInfo = await superagent.get('https://api.github.com/user') - .proxy(proxy) - .set('User-Agent', 'Hydro-OAuth') - .set('Authorization', `token ${t}`); - const ret = { - email: userInfo.body.email, - bio: userInfo.body.bio, - uname: [userInfo.body.name, userInfo.body.login], - }; - this.response.redirect = s.redirect; - await token.del(s._id, token.TYPE_OAUTH); - return ret; - } - - @param('state', Types.String) - @param('code', Types.String) - @param('error', Types.String) - async google(domainId: string, state: string, code: string, error: string) { - if (error) throw new UserFacingError(error); - const [ - [appid, secret, url, proxy], - s, - ] = await Promise.all([ - system.getMany([ - 'oauth.googleappid', 'oauth.googlesecret', 'server.url', 'proxy', - ]), - token.get(state, token.TYPE_OAUTH), - ]); - const res = await superagent.post('https://oauth2.googleapis.com/token') - .proxy(proxy) - .send({ - client_id: appid, - client_secret: secret, - code, - grant_type: 'authorization_code', - redirect_uri: `${url}oauth/google/callback`, - }); - const payload = global.Hydro.lib.jwt.decode(res.body.id_token); - await token.del(state, token.TYPE_OAUTH); - this.response.redirect = s.redirect; - return { - email: payload.email, - uname: [payload.given_name, payload.name, payload.family_name], - viewLang: payload.locale.replace('-', '_'), - }; - } - async get(args: any) { let r; - // @ts-ignore - if (args.type === 'github') r = await this.github(args); - // @ts-ignore - else if (args.type === 'google') r = await this.google(args); + if (global.Hydro.lib[`oauth_${args.type}`]) r = await global.Hydro.lib[`oauth_${args.type}`].callback(args); else throw new UserFacingError('Oauth type'); const udoc = await user.getByEmail('system', r.email); if (udoc) { diff --git a/hydro/model/setting.ts b/hydro/model/setting.ts index c424e887a..2a4aa0eb4 100644 --- a/hydro/model/setting.ts +++ b/hydro/model/setting.ts @@ -129,11 +129,6 @@ SystemSetting( Setting('setting_session', 'session.secure', null, false, 'boolean', 'session.secure'), Setting('setting_session', 'session.saved_expire_seconds', null, 3600 * 24 * 30, 'number', 'Saved session expire seconds'), Setting('setting_session', 'session.unsaved_expire_seconds', null, 3600 * 3, 'number', 'Unsaved session expire seconds'), - Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'), - Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret', null, FLAG_SECRET), - Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID', null), - Setting('setting_oauth', 'oauth.googlesecret', null, null, 'text', 'Google Oauth Secret', null, FLAG_SECRET), - Setting('setting_proxy', 'proxy', null, null, 'text', 'Proxy Server URL'), Setting('setting_storage', 'db.ver', null, 1, 'number', 'Database version', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'user', null, 1, 'number', 'User Counter', null, FLAG_DISABLED | FLAG_HIDDEN), ); diff --git a/hydro/service/server.ts b/hydro/service/server.ts index 7f56b736d..4fc432b42 100644 --- a/hydro/service/server.ts +++ b/hydro/service/server.ts @@ -4,7 +4,7 @@ import path from 'path'; import os from 'os'; import http from 'http'; import moment from 'moment-timezone'; -import { isSafeInteger, Dictionary } from 'lodash'; +import { isSafeInteger, Dictionary, filter } from 'lodash'; import { ObjectID } from 'mongodb'; import Koa from 'koa'; import morgan from 'koa-morgan'; @@ -232,6 +232,8 @@ export class Handler { domain: DomainDoc; + loginMethods: any; + constructor(ctx: Koa.Context) { this.ctx = ctx; this.request = { @@ -377,6 +379,12 @@ export class Handler { } this.csrfToken = this.getCsrfToken(this.session._id || String.random(32)); this.UIContext.csrfToken = this.csrfToken; + this.loginMethods = filter(Object.keys(global.Hydro.lib), (str) => str.startsWith('oauth_')) + .map((key) => ({ + id: key.split('_')[1], + icon: global.Hydro.lib[key].icon, + text: global.Hydro.lib[key].text, + })); } async finish() { diff --git a/hydro/utils.ts b/hydro/utils.ts index 2c7ee2d9b..d5a5cec3d 100644 --- a/hydro/utils.ts +++ b/hydro/utils.ts @@ -2,8 +2,8 @@ import fs from 'fs'; import cluster from 'cluster'; import path from 'path'; import * as superagent from 'superagent'; +import proxyAgent from 'proxy-agent'; import serialize from 'serialize-javascript'; -import proxy from 'superagent-proxy'; import Lru from 'lru-cache'; declare global { @@ -41,7 +41,51 @@ export function lrucache(target: any, funcName: string, obj: any) { return obj; } -proxy(superagent); +namespace Proxy { + function getUrl() { + return this._url; + } + function setUrl(v: string) { + this._url = v; + // eslint-disable-next-line no-use-before-define + proxy.call(this, this._proxyUri); + } + function setupUrl(req: superagent.Request) { + const desc = Object.getOwnPropertyDescriptor(req, 'url'); + if (desc.get === getUrl && desc.set === setUrl) return; + // @ts-ignore + req._url = req.url; + desc.get = getUrl; + desc.set = setUrl; + delete desc.value; + delete desc.writable; + Object.defineProperty(req, 'url', desc); + } + function proxy(uri: string) { + if (!uri) return this; + setupUrl(this); + const agent = proxyAgent(uri); + if (agent) this.agent(agent); + this._proxyUri = uri; + return this; + } + export function setup(agent, uri = '') { + const Request = agent.Request; + if (Request) { + Request.prototype.proxy = proxy; + return agent; + } + return proxy.call(agent, uri); + } +} + +declare module 'superagent' { + interface Request { + proxy(url: string): this; + } +} + +Proxy.setup(superagent); if (!cluster.worker) { // @ts-ignore diff --git a/package.json b/package.json index 2ee7e3dfc..8602f3918 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.12.0", + "version": "2.12.1", "main": "dist/loader.js", "bin": "bin/hydrooj.js", "repository": "https://github.com/hydro-dev/Hydro.git", @@ -35,11 +35,11 @@ "nodemailer": "^6.4.10", "nunjucks": "^3.2.2", "prismjs": "^1.20.0", + "proxy-agent": "^3.1.1", "ps-list": "^7.2.0", "serialize-javascript": "^4.0.0", "sockjs": "^0.3.20", "superagent": "^5.2.2", - "superagent-proxy": "^2.0.0", "systeminformation": "^4.26.10", "yargs": "^15.4.1" }, @@ -64,7 +64,6 @@ "@types/serialize-javascript": "^4.0.0", "@types/sockjs": "^0.3.32", "@types/superagent": "^4.1.8", - "@types/superagent-proxy": "^2.0.0", "@types/yargs": "^15.0.5", "@typescript-eslint/eslint-plugin": "^3.7.0", "@typescript-eslint/parser": "^3.7.0", diff --git a/yarn.lock b/yarn.lock index a6ba3465b..700b04cf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -310,14 +310,7 @@ dependencies: "@types/node" "*" -"@types/superagent-proxy@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/superagent-proxy/-/superagent-proxy-2.0.0.tgz#9dd72c2b875ab56c4e6bd42435542fb040aa75b8" - integrity sha512-EiC8z93IkQhTYtmQ4/g/CoXUlZRmrmfOwMkjO3/lzxEmfShaPYlkk7vtgMpFkKw8V/1g13fPwR98dZuieHeBUw== - dependencies: - "@types/superagent" "*" - -"@types/superagent@*", "@types/superagent@^4.1.8": +"@types/superagent@^4.1.8": version "4.1.8" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.8.tgz#f663dcdd24705e07fce055003ace9b824f2a51c9" integrity sha512-iol9KxQ7SLHatBJUiZ4uABrS4VS1frLjqPednxZz82eoCzo3Uy3TOH0p0ZIBbfBj8E/xqOtvizjBs9h7xi/l2g== @@ -2301,7 +2294,7 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -proxy-agent@3: +proxy-agent@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.1.tgz#7e04e06bf36afa624a1540be247b47c970bd3014" integrity sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw== @@ -2690,14 +2683,6 @@ strip-json-comments@^3.1.0: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -superagent-proxy@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/superagent-proxy/-/superagent-proxy-2.0.0.tgz#9f57515cd660e2e9ce55c0e6bd70f92eb07c3ee0" - integrity sha512-TktJma5jPdiH1BNN+reF/RMW3b8aBTCV7KlLFV0uYcREgNf3pvo7Rdt564OcFHwkGb3mYEhHuWPBhSbOwiNaYw== - dependencies: - debug "^3.1.0" - proxy-agent "3" - superagent@^5.2.2: version "5.3.1" resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1"