blob: d6f4831226d7477c0d16603fbb723af828829543 [file] [log] [blame]
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -05001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18import argon2 from 'argon2';
19import { Router } from 'express';
20import asyncHandler from 'express-async-handler';
21import { ParamsDictionary, Request } from 'express-serve-static-core';
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050022import { AccessToken, AdminCredentials, HttpStatusCode } from 'jami-web-common';
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050023import { Container } from 'typedi';
24
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050025import { checkAdminSetup } from '../middleware/setup.js';
Misha Krieger-Raynauldb933fbb2022-11-15 15:11:09 -050026import { AdminAccount } from '../storage/admin-account.js';
27import { signJwt } from '../utils/jwt.js';
28
29const adminAccount = Container.get(AdminAccount);
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050030
31export const setupRouter = Router();
32
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050033setupRouter.get('/check', (_req, res) => {
Misha Krieger-Raynauldb933fbb2022-11-15 15:11:09 -050034 const isSetupComplete = adminAccount.get() !== undefined;
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050035 res.send({ isSetupComplete });
36});
37
38setupRouter.post(
39 '/admin/create',
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050040 asyncHandler(async (req: Request<ParamsDictionary, string, Partial<AdminCredentials>>, res) => {
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050041 const { password } = req.body;
42 if (password === undefined) {
43 res.status(HttpStatusCode.BadRequest).send('Missing password in body');
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050044 return;
45 }
46
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050047 if (password === '') {
48 res.status(HttpStatusCode.BadRequest).send('Password may not be empty');
49 return;
50 }
51
Misha Krieger-Raynauldb933fbb2022-11-15 15:11:09 -050052 const isAdminCreated = adminAccount.get() !== undefined;
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050053 if (isAdminCreated) {
54 res.status(HttpStatusCode.Conflict).send('Admin already exists');
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050055 return;
56 }
57
58 const hashedPassword = await argon2.hash(password, { type: argon2.argon2id });
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050059
Misha Krieger-Raynauldb933fbb2022-11-15 15:11:09 -050060 adminAccount.set(hashedPassword);
61 await adminAccount.save();
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050062
63 res.sendStatus(HttpStatusCode.Created);
64 })
65);
66
67// Every request handler after this line will be submitted to this middleware
68// in order to ensure that the admin account is set up before proceeding with
Misha Krieger-Raynauldb933fbb2022-11-15 15:11:09 -050069// setup-related requests
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050070setupRouter.use(checkAdminSetup);
71
72setupRouter.post(
73 '/admin/login',
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050074 asyncHandler(async (req: Request<ParamsDictionary, AccessToken | string, Partial<AdminCredentials>>, res) => {
75 const { password } = req.body;
76 if (password === undefined) {
77 res.status(HttpStatusCode.BadRequest).send('Missing password in body');
78 return;
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050079 }
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050080
81 const hashedPassword = adminAccount.get();
82 if (hashedPassword === undefined) {
83 res.status(HttpStatusCode.InternalServerError).send('Admin password not found');
84 return;
85 }
86
87 const isPasswordVerified = await argon2.verify(hashedPassword, password);
88 if (!isPasswordVerified) {
89 res.status(HttpStatusCode.Forbidden).send('Incorrect password');
90 return;
91 }
92
93 const jwt = await signJwt('admin');
94 res.send({ accessToken: jwt });
95 })
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -050096);