smc-hub
Version:
CoCalc: Backend webserver component
77 lines (66 loc) • 1.91 kB
text/typescript
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
/*
Authentication.
*/
import { List, Map } from "immutable";
import basic_auth from "basic-auth";
import { verify } from "password-hash";
import { startswith } from "smc-util/misc";
import { Logger } from "./types";
export function is_authenticated(opts: {
req: any;
res: any;
path: string;
auth?: Map<string, any>; // immutable.js map -- {path:[{name:[string], pass:[password-hash]}, ...], ...}
logger?: Logger;
}): boolean {
if (opts.auth == null) {
return true; // no authentication needed
}
// strip any /'s from beginning of opts.path (auth path's are assumed relative)
while (opts.path[0] === "/") {
opts.path = opts.path.slice(1);
}
let auth_info: any = undefined;
opts.auth.forEach(function (info, path: string): boolean | undefined {
if (startswith(opts.path, path)) {
auth_info = info;
return false;
}
}); // break
if (auth_info == null) {
// don't need auth for this path
return true;
}
if (!List.isList(auth_info)) {
opts.res.statusCode = 401;
opts.res.end(
"auth is misconfigured -- invalid auth field in the public_paths database."
);
return false;
}
const credentials = basic_auth(opts.req);
let fail = true;
if (credentials != null && credentials.name && credentials.pass) {
for (let i = 0; i < auth_info.size; i++) {
const x = auth_info.get(i);
if (x.get("name") === credentials.name) {
if (verify(credentials.pass, x.get("pass"))) {
fail = false;
}
break;
}
}
}
if (fail) {
opts.res.statusCode = 401;
opts.res.setHeader("WWW-Authenticate", 'Basic realm="cocalc.com"');
opts.res.end("Access denied");
return false;
}
// access granted
return true;
}