core: oauth: enhancement

This commit is contained in:
undefined committed 2022-02-09 18:44:41 +08:00
1 parent 04ec44dab9
commit b1f90ab5a1
10 files changed
+55 -26

No files matched your search

+2 -2
View File
@@ -30,8 +30,8 @@
"@types/cross-spawn": "^6.0.2", "@types/cross-spawn": "^6.0.2",
"@types/node": "^16.11.22", "@types/node": "^16.11.22",
"@types/semver": "^7.3.9", "@types/semver": "^7.3.9",
"@typescript-eslint/eslint-plugin": "5.10.2", "@typescript-eslint/eslint-plugin": "5.11.0",
"@typescript-eslint/parser": "5.10.2", "@typescript-eslint/parser": "5.11.0",
"cac": "^6.7.12", "cac": "^6.7.12",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "3.4.9", "version": "3.4.10",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "src/loader", "main": "src/loader",
"module": "src/loader", "module": "src/loader",
@@ -15,7 +15,7 @@
"@graphql-tools/schema": "^8.3.1", "@graphql-tools/schema": "^8.3.1",
"@hydrooj/utils": "workspace:*", "@hydrooj/utils": "workspace:*",
"adm-zip": "0.5.5", "adm-zip": "0.5.5",
"ajv": "^8.9.0", "ajv": "^8.10.0",
"ansi_up": "^5.1.0", "ansi_up": "^5.1.0",
"cac": "^6.7.12", "cac": "^6.7.12",
"cookies": "^0.8.0", "cookies": "^0.8.0",
@@ -34,7 +34,7 @@
"koa-router": "^10.1.1", "koa-router": "^10.1.1",
"koa-static-cache": "^5.1.4", "koa-static-cache": "^5.1.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lru-cache": "^6.0.0", "lru-cache": "^7.3.0",
"mime-types": "^2.1.34", "mime-types": "^2.1.34",
"minio": "7.0.25", "minio": "7.0.25",
"moment-timezone": "^0.5.34", "moment-timezone": "^0.5.34",
+17 -5
View File
@@ -6,7 +6,7 @@ import {
SystemError, UserAlreadyExistError, UserFacingError, SystemError, UserAlreadyExistError, UserFacingError,
UserNotFoundError, ValidationError, VerifyPasswordError, UserNotFoundError, ValidationError, VerifyPasswordError,
} from '../error'; } from '../error';
import { User } from '../interface'; import { OAuthUserResponse, Udoc, User } from '../interface';
import avatar from '../lib/avatar'; import avatar from '../lib/avatar';
import { sendMail } from '../lib/mail'; import { sendMail } from '../lib/mail';
import { isEmail, isPassword, isUname } from '../lib/validator'; import { isEmail, isPassword, isUname } from '../lib/validator';
@@ -360,14 +360,23 @@ class OauthCallbackHandler extends Handler {
noCheckPermView = true; noCheckPermView = true;
async get(args: any) { async get(args: any) {
let r; if (!global.Hydro.lib[`oauth_${args.type}`]) throw new UserFacingError('Oauth type');
if (global.Hydro.lib[`oauth_${args.type}`]) r = await global.Hydro.lib[`oauth_${args.type}`].callback.call(this, args); const r = await global.Hydro.lib[`oauth_${args.type}`].callback.call(this, args) as OAuthUserResponse;
else throw new UserFacingError('Oauth type');
const uid = await oauth.get(r._id); const uid = await oauth.get(r._id);
if (uid) { if (uid) {
await user.setById(uid, { loginat: new Date(), loginip: this.request.ip });
this.session.uid = uid; this.session.uid = uid;
this.session.scope = PERM.PERM_ALL.toString(); this.session.scope = PERM.PERM_ALL.toString();
} else { } else {
if (r.email) {
const udoc = await user.getByEmail('system', r.email);
if (udoc) {
await user.setById(udoc._id, { loginat: new Date(), loginip: this.request.ip });
this.session.uid = udoc._id;
this.session.scpoe = PERM.PERM_ALL.toString();
return;
}
}
this.checkPriv(PRIV.PRIV_REGISTER_USER); this.checkPriv(PRIV.PRIV_REGISTER_USER);
let username = ''; let username = '';
r.uname = r.uname || []; r.uname = r.uname || [];
@@ -384,11 +393,14 @@ class OauthCallbackHandler extends Handler {
r.email, username, String.random(32), r.email, username, String.random(32),
undefined, this.request.ip, undefined, this.request.ip,
); );
const $set: any = { const $set: Partial<Udoc> = {
oauth: args.type, oauth: args.type,
loginat: new Date(),
loginip: this.request.ip,
}; };
if (r.bio) $set.bio = r.bio; if (r.bio) $set.bio = r.bio;
if (r.viewLang) $set.viewLang = r.viewLang; if (r.viewLang) $set.viewLang = r.viewLang;
if (r.avatar) $set.avatar = r.avatar;
await Promise.all([ await Promise.all([
user.setById(_id, $set), user.setById(_id, $set),
oauth.set(r.email, _id), oauth.set(r.email, _id),
+9
View File
@@ -55,6 +55,15 @@ export interface Setting {
flag: number, flag: number,
} }
export interface OAuthUserResponse {
_id: string;
avatar?: string;
email?: string;
bio?: string;
uname?: string[];
viewLang?: string;
}
export interface Udoc extends Dictionary<any> { export interface Udoc extends Dictionary<any> {
_id: number; _id: number;
mail: string; mail: string;
+13 -10
View File
@@ -1,38 +1,40 @@
import 'hydrooj'; import 'hydrooj';
import * as superagent from 'superagent'; import superagent from 'superagent';
declare module 'hydrooj' { declare module 'hydrooj' {
interface SystemKeys { interface SystemKeys {
'login-with-github.id': string, 'login-with-github.id': string;
'login-with-github.secret': string, 'login-with-github.secret': string;
'login-with-github.endpoint': string;
} }
interface Lib { interface Lib {
oauth_github: typeof import('./lib'), oauth_github: typeof import('./lib');
} }
} }
async function get() { async function get() {
const { system, token } = global.Hydro.model; const { system, token } = global.Hydro.model;
const [appid, [state]] = await Promise.all([ const [[appid, endpoint = 'https://github.com'], [state]] = await Promise.all([
system.get('login-with-github.id'), system.getMany(['login-with-github.id', 'login-with-github.endpoint']),
token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }), token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }),
]); ]);
this.response.redirect = `https://github.com/login/oauth/authorize?client_id=${appid}&state=${state}`; this.response.redirect = `${endpoint}/login/oauth/authorize?client_id=${appid}&state=${state}`;
} }
async function callback({ state, code }) { async function callback({ state, code }) {
const { system, token } = global.Hydro.model; const { system, token } = global.Hydro.model;
const { UserFacingError } = global.Hydro.error; const { UserFacingError } = global.Hydro.error;
const [[appid, secret, url], s] = await Promise.all([ const [[appid, secret, endpoint, url], s] = await Promise.all([
system.getMany([ system.getMany([
'login-with-github.id', 'login-with-github.id',
'login-with-github.secret', 'login-with-github.secret',
'login-with-github.endpoint',
'server.url', 'server.url',
]), ]),
token.get(state, token.TYPE_OAUTH), token.get(state, token.TYPE_OAUTH),
]); ]);
const res = await superagent.post('https://github.com/login/oauth/access_token') const res = await superagent.post(`${endpoint || 'https://github.com'}/login/oauth/access_token`)
.send({ .send({
client_id: appid, client_id: appid,
client_secret: secret, client_secret: secret,
@@ -47,7 +49,7 @@ async function callback({ state, code }) {
); );
} }
const t = res.body.access_token; const t = res.body.access_token;
const userInfo = await superagent.get('https://api.github.com/user') const userInfo = await superagent.get(`${endpoint || 'https://api.github.com'}/user`)
.set('User-Agent', 'Hydro-OAuth') .set('User-Agent', 'Hydro-OAuth')
.set('Authorization', `token ${t}`); .set('Authorization', `token ${t}`);
const ret = { const ret = {
@@ -55,6 +57,7 @@ async function callback({ state, code }) {
email: userInfo.body.email, email: userInfo.body.email,
bio: userInfo.body.bio, bio: userInfo.body.bio,
uname: [userInfo.body.name, userInfo.body.login], uname: [userInfo.body.name, userInfo.body.login],
avatar: `github:${userInfo.body.login}`,
}; };
this.response.redirect = s.redirect; this.response.redirect = s.redirect;
await token.del(s._id, token.TYPE_OAUTH); await token.del(s._id, token.TYPE_OAUTH);
+2 -2
View File
@@ -1,13 +1,13 @@
{ {
"name": "@hydrooj/login-with-github", "name": "@hydrooj/login-with-github",
"version": "0.1.0", "version": "0.1.1",
"main": "package.json", "main": "package.json",
"repository": "git@github.com:hydro-dev/Hydro.git", "repository": "git@github.com:hydro-dev/Hydro.git",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"preferUnplugged": true, "preferUnplugged": true,
"devDependencies": { "devDependencies": {
"@types/superagent": "^4.1.14" "@types/superagent": "^4.1.15"
}, },
"peerDependencies": { "peerDependencies": {
"hydrooj": "*" "hydrooj": "*"
+6 -1
View File
@@ -7,4 +7,9 @@ secret:
type: text type: text
category: system category: system
desc: Github OAuth Secret desc: Github OAuth Secret
name: secret name: secret
endpoint:
type: text
category: system
desc: Github Api Endpoint
name: endpoint
+1 -1
View File
@@ -7,7 +7,7 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"preferUnplugged": true, "preferUnplugged": true,
"devDependencies": { "devDependencies": {
"@types/superagent": "^4.1.14" "@types/superagent": "^4.1.15"
}, },
"peerDependencies": { "peerDependencies": {
"hydrooj": "*" "hydrooj": "*"
+1 -1
View File
@@ -7,7 +7,7 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"preferUnplugged": true, "preferUnplugged": true,
"devDependencies": { "devDependencies": {
"@types/superagent": "^4.1.14" "@types/superagent": "^4.1.15"
}, },
"peerDependencies": { "peerDependencies": {
"hydrooj": "*" "hydrooj": "*"
+1 -1
View File
@@ -7,7 +7,7 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"preferUnplugged": true, "preferUnplugged": true,
"dependencies": { "dependencies": {
"ws": "^8.4.0" "ws": "^8.5.0"
}, },
"devDependencies": { "devDependencies": {
"@types/ws": "^8.2.2" "@types/ws": "^8.2.2"