Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/src/config/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export default class Globals {
static cookieName = "ctfnote-auth";
static userAgent = "CTFNote";

static hedgedocCookieName = "connect.sid";
static hedgedocAuth = process.env.MD_AUTH == "true" || true;

static adminRights = [Rights.ADMIN_ALL];
static defaultRights = [];

Expand All @@ -36,5 +39,6 @@ export default class Globals {
"allow-registration",
Globals.allowRegistration
);
Globals.hedgedocAuth = await PersistentConfiguration.setIfNotSet("hedgedoc-auth", Globals.hedgedocAuth);
}
}
7 changes: 7 additions & 0 deletions api/src/controllers/auth.controller/login.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import PasswordUtil from "../../utils/password";
import authLimiter from "../../ratelimits/auth";
import { getConnection } from "typeorm";
import SessionManager from "../../utils/session";
import PadService from "../../services/pad";

function deny(res: Response) {
return res.status(403).json({ errors: [{ msg: `Invalid username/password` }] });
Expand Down Expand Up @@ -44,6 +45,12 @@ const LoginAction: IRoute = {

if (!session) return deny(res);

//try to authenticate with HedgeDoc
let padCookie = await PadService.login(username, password);
if (padCookie != null) {
res.setHeader("Set-Cookie", padCookie);
}

res.cookie(Globals.cookieName, session.uuid, {
expires: session.expiresAt,
httpOnly: true,
Expand Down
1 change: 1 addition & 0 deletions api/src/controllers/auth.controller/logout.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const LogoutAction: IRoute = {

if (logout) await SessionManager.invalidateSession(req.cookies[Globals.cookieName]);

res.clearCookie(Globals.hedgedocCookieName);
return res.status(204).send();
},
};
Expand Down
16 changes: 14 additions & 2 deletions api/src/controllers/auth.controller/register.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import authLimiter from "../../ratelimits/auth";

import makeSlug from "../../utils/slugify";
import SessionManager from "../../utils/session";
import Rights from "../../config/rights";
import PersistentConfiguration from "../../config/persitent";
import PadService from "../../services/pad";

const RegisterAction: IRoute = {
middlewares: [
Expand Down Expand Up @@ -42,14 +42,26 @@ const RegisterAction: IRoute = {
const hash = await PasswordUtil.hash(password);

const firstAccount = (await userRepo.count()) === 0;
let user = userRepo.create({ username, slug, password: hash, rights: firstAccount ? Globals.adminRights : Globals.defaultRights });
let user = userRepo.create({
username,
slug,
password: hash,
rights: firstAccount ? Globals.adminRights : Globals.defaultRights,
});

try {
user = await userRepo.save(user);
} catch (e) {
return res.status(409).json({ errors: [{ msg: "A user with that username already exists" }] });
}

//try to authenticate with HedgeDoc
let padCookie = await PadService.register(username, req.body.password);
if (padCookie != null) {
padCookie = await PadService.login(username, req.body.password);
res.setHeader("Set-Cookie", padCookie);
}

const session = await SessionManager.generateSession(user.slug);
res.cookie(Globals.cookieName, session.uuid, {
expires: session.expiresAt,
Expand Down
2 changes: 2 additions & 0 deletions api/src/controllers/config.controller/update.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ const UpdateConfigAction: IRoute = {
"md-create-url": mdCreateUrl,
"md-show-url": mdShowUrl,
"allow-registration": allowRegistration,
"hedgedoc-auth": hedgedocAuth,
} = req.body;

if (mdCreateUrl != null) await PersistentConfiguration.set("md-create-url", mdCreateUrl);
if (mdShowUrl != null) await PersistentConfiguration.set("md-show-url", mdShowUrl);
if (allowRegistration != null) await PersistentConfiguration.set("allow-registration", allowRegistration);
if (hedgedocAuth != null) await PersistentConfiguration.set("hedgedoc-auth", hedgedocAuth);

return res.status(200).json(await PersistentConfiguration.list());
},
Expand Down
56 changes: 56 additions & 0 deletions api/src/services/pad.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Axios from "axios";
import logger from "../config/logger";
import PersistentConfiguration from "../config/persitent";
import querystring from "querystring";
import Globals from "../config/globals";

export default class PadService {
/**
Expand All @@ -22,4 +24,58 @@ export default class PadService {
return "#";
}
}

private static async baseUrl(): Promise<string> {
let cleanUrl: string = await PersistentConfiguration.get("md-create-url");
cleanUrl = cleanUrl.slice(0, -4); //remove '/new' for clean url
return cleanUrl;
}

private static async authPad(username: string, password: string, url: URL): Promise<string> {
if (!Globals.hedgedocAuth) {
return null;
}

let domain: string;
//if domain does not end in '.[tld]', it will be rejected
//so we add '.local' manually
if (url.hostname.split(".").length == 1) {
domain = `${url.hostname}.local`;
} else {
domain = url.hostname;
}

const email = `${username}@${domain}`;

try {
const res = await Axios.post(
url.toString(),
querystring.stringify({
email: email,
password: password,
}),
{
validateStatus: (status) => status === 302,
maxRedirects: 0,
timeout: 5000,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
}
);
return res.headers["set-cookie"];
} catch (e) {
logger.warn(`Could not auth to pad service: '${url.toString()}': `);
logger.fatal(e);
return null;
}
}

static async register(username: string, password: string): Promise<string> {
const authUrl = new URL(`${await this.baseUrl()}/register`);
return this.authPad(username, password, authUrl);
}

static async login(username: string, password: string): Promise<string> {
const authUrl = new URL(`${await this.baseUrl()}/login`);
return this.authPad(username, password, authUrl);
}
}