mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
更新oauth架构
This commit is contained in:
1 parent
d184ca9ea1
commit
b3af28dc43
7 files changed
+64
-132
No files matched your search
@@ -26,6 +26,7 @@ module.exports = {
|
|||||||
'no-bitwise': 'off',
|
'no-bitwise': 'off',
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'no-extend-native': 'off',
|
'no-extend-native': 'off',
|
||||||
|
'no-inner-declarations': 'off',
|
||||||
'no-multi-assign': 'off',
|
'no-multi-assign': 'off',
|
||||||
'no-nested-ternary': 'off',
|
'no-nested-ternary': 'off',
|
||||||
'no-param-reassign': 'off',
|
'no-param-reassign': 'off',
|
||||||
|
|||||||
+4
-104
@@ -1,4 +1,3 @@
|
|||||||
import superagent from 'superagent';
|
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import {
|
import {
|
||||||
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
||||||
@@ -22,12 +21,6 @@ import paginate from '../lib/paginate';
|
|||||||
|
|
||||||
class UserLoginHandler extends Handler {
|
class UserLoginHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
const [loginWithGithub, loginWithGoogle] = await system.getMany(
|
|
||||||
['oauth.githubappid', 'oauth.googleappid'],
|
|
||||||
);
|
|
||||||
this.response.body = {
|
|
||||||
loginWithGithub, loginWithGoogle,
|
|
||||||
};
|
|
||||||
this.response.template = 'user_login.html';
|
this.response.template = 'user_login.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,109 +215,16 @@ class UserSearchHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OauthHandler extends Handler {
|
class OauthHandler extends Handler {
|
||||||
async github() {
|
@param('type', Types.String)
|
||||||
const [appid, [state]] = await Promise.all([
|
async get(domainId: string, type: string) {
|
||||||
system.get('oauth.githubappid'),
|
if (global.Hydro.lib[`oauth_${type}`]) await global.Hydro.lib[`oauth_${type}`].get.call(this);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OauthCallbackHandler extends Handler {
|
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) {
|
async get(args: any) {
|
||||||
let r;
|
let r;
|
||||||
// @ts-ignore
|
if (global.Hydro.lib[`oauth_${args.type}`]) r = await global.Hydro.lib[`oauth_${args.type}`].callback(args);
|
||||||
if (args.type === 'github') r = await this.github(args);
|
|
||||||
// @ts-ignore
|
|
||||||
else if (args.type === 'google') r = await this.google(args);
|
|
||||||
else throw new UserFacingError('Oauth type');
|
else throw new UserFacingError('Oauth type');
|
||||||
const udoc = await user.getByEmail('system', r.email);
|
const udoc = await user.getByEmail('system', r.email);
|
||||||
if (udoc) {
|
if (udoc) {
|
||||||
|
|||||||
@@ -129,11 +129,6 @@ SystemSetting(
|
|||||||
Setting('setting_session', 'session.secure', null, false, 'boolean', 'session.secure'),
|
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.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_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', '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),
|
Setting('setting_storage', 'user', null, 1, 'number', 'User Counter', null, FLAG_DISABLED | FLAG_HIDDEN),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import path from 'path';
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { isSafeInteger, Dictionary } from 'lodash';
|
import { isSafeInteger, Dictionary, filter } from 'lodash';
|
||||||
import { ObjectID } from 'mongodb';
|
import { ObjectID } from 'mongodb';
|
||||||
import Koa from 'koa';
|
import Koa from 'koa';
|
||||||
import morgan from 'koa-morgan';
|
import morgan from 'koa-morgan';
|
||||||
@@ -232,6 +232,8 @@ export class Handler {
|
|||||||
|
|
||||||
domain: DomainDoc;
|
domain: DomainDoc;
|
||||||
|
|
||||||
|
loginMethods: any;
|
||||||
|
|
||||||
constructor(ctx: Koa.Context) {
|
constructor(ctx: Koa.Context) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.request = {
|
this.request = {
|
||||||
@@ -377,6 +379,12 @@ export class Handler {
|
|||||||
}
|
}
|
||||||
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
|
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
|
||||||
this.UIContext.csrfToken = this.csrfToken;
|
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() {
|
async finish() {
|
||||||
|
|||||||
+46
-2
@@ -2,8 +2,8 @@ import fs from 'fs';
|
|||||||
import cluster from 'cluster';
|
import cluster from 'cluster';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as superagent from 'superagent';
|
import * as superagent from 'superagent';
|
||||||
|
import proxyAgent from 'proxy-agent';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
import proxy from 'superagent-proxy';
|
|
||||||
import Lru from 'lru-cache';
|
import Lru from 'lru-cache';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -41,7 +41,51 @@ export function lrucache(target: any, funcName: string, obj: any) {
|
|||||||
return obj;
|
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) {
|
if (!cluster.worker) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.12.0",
|
"version": "2.12.1",
|
||||||
"main": "dist/loader.js",
|
"main": "dist/loader.js",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
"nodemailer": "^6.4.10",
|
"nodemailer": "^6.4.10",
|
||||||
"nunjucks": "^3.2.2",
|
"nunjucks": "^3.2.2",
|
||||||
"prismjs": "^1.20.0",
|
"prismjs": "^1.20.0",
|
||||||
|
"proxy-agent": "^3.1.1",
|
||||||
"ps-list": "^7.2.0",
|
"ps-list": "^7.2.0",
|
||||||
"serialize-javascript": "^4.0.0",
|
"serialize-javascript": "^4.0.0",
|
||||||
"sockjs": "^0.3.20",
|
"sockjs": "^0.3.20",
|
||||||
"superagent": "^5.2.2",
|
"superagent": "^5.2.2",
|
||||||
"superagent-proxy": "^2.0.0",
|
|
||||||
"systeminformation": "^4.26.10",
|
"systeminformation": "^4.26.10",
|
||||||
"yargs": "^15.4.1"
|
"yargs": "^15.4.1"
|
||||||
},
|
},
|
||||||
@@ -64,7 +64,6 @@
|
|||||||
"@types/serialize-javascript": "^4.0.0",
|
"@types/serialize-javascript": "^4.0.0",
|
||||||
"@types/sockjs": "^0.3.32",
|
"@types/sockjs": "^0.3.32",
|
||||||
"@types/superagent": "^4.1.8",
|
"@types/superagent": "^4.1.8",
|
||||||
"@types/superagent-proxy": "^2.0.0",
|
|
||||||
"@types/yargs": "^15.0.5",
|
"@types/yargs": "^15.0.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
||||||
"@typescript-eslint/parser": "^3.7.0",
|
"@typescript-eslint/parser": "^3.7.0",
|
||||||
|
|||||||
@@ -310,14 +310,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/superagent-proxy@^2.0.0":
|
"@types/superagent@^4.1.8":
|
||||||
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":
|
|
||||||
version "4.1.8"
|
version "4.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.8.tgz#f663dcdd24705e07fce055003ace9b824f2a51c9"
|
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.8.tgz#f663dcdd24705e07fce055003ace9b824f2a51c9"
|
||||||
integrity sha512-iol9KxQ7SLHatBJUiZ4uABrS4VS1frLjqPednxZz82eoCzo3Uy3TOH0p0ZIBbfBj8E/xqOtvizjBs9h7xi/l2g==
|
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"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||||
|
|
||||||
proxy-agent@3:
|
proxy-agent@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.1.tgz#7e04e06bf36afa624a1540be247b47c970bd3014"
|
resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.1.tgz#7e04e06bf36afa624a1540be247b47c970bd3014"
|
||||||
integrity sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==
|
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"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
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:
|
superagent@^5.2.2:
|
||||||
version "5.3.1"
|
version "5.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1"
|
resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1"
|
||||||
|
|||||||
Reference in new issue
Block a user