From cb9d28128fb716797f092f75ac1fdfddad54993d Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 20 Jul 2020 16:34:35 +0200 Subject: [PATCH] Update codebase to latest wms-core and add recovery email add form --- config/default.ts | 4 +- config/production.ts | 5 +- src/Aldap.ts | 101 ------------------------- src/App.ts | 47 +++++++++++- src/LDAPServerComponent.ts | 20 +---- src/controllers/AccountController.ts | 21 +++++ src/controllers/AuthController.ts | 25 +++--- src/controllers/MagicLinkActionType.ts | 3 + src/controllers/MagicLinkController.ts | 35 +++++++++ src/controllers/PreLaunchWall.ts | 51 ------------- src/migrations.ts | 13 ---- src/models/UserPassword.ts | 30 +++++--- src/models/Username.ts | 26 ++++--- views/account.njk | 31 ++++++++ views/layouts/base.njk | 22 ++++-- views/login.njk | 2 +- views/prelaunch-wall.njk | 19 ----- views/register.njk | 4 +- 18 files changed, 208 insertions(+), 251 deletions(-) delete mode 100644 src/Aldap.ts create mode 100644 src/controllers/AccountController.ts create mode 100644 src/controllers/MagicLinkActionType.ts create mode 100644 src/controllers/MagicLinkController.ts delete mode 100644 src/controllers/PreLaunchWall.ts delete mode 100644 src/migrations.ts create mode 100644 views/account.njk delete mode 100644 views/prelaunch-wall.njk diff --git a/config/default.ts b/config/default.ts index 863e1da..9ecdeb9 100644 --- a/config/default.ts +++ b/config/default.ts @@ -24,7 +24,8 @@ export default Object.assign(require("wms-core/config/default").default, { session: { secret: "very_secret_not_known", cookie: { - secure: false + secure: false, + maxAge: 30 * 24 * 3600 * 1000, } }, mail: { @@ -41,5 +42,4 @@ export default Object.assign(require("wms-core/config/default").default, { cache: false }, approval_mode: false, - 'prelaunch-password': '$argon2i$v=19$m=4096,t=3,p=1$V7njt+IBmIQ/epc7tuQcfA$ypJCNauYSPrjOhtb5UqTbRlqCHkEGikBApOrYmbdYC0', }); \ No newline at end of file diff --git a/config/production.ts b/config/production.ts index 36feb69..f509007 100644 --- a/config/production.ts +++ b/config/production.ts @@ -1,9 +1,8 @@ export default Object.assign(require("wms-core/config/production").default, { - 'prelaunch-password': 'CHANGE ME', log_level: "DEBUG", db_log_level: "ERROR", - public_url: "https://watch-my.stream", - public_websocket_url: "wss://watch-my.stream", + public_url: "https://aldap.toot.party", + public_websocket_url: "wss://aldap.toot.party", session: { cookie: { secure: true diff --git a/src/Aldap.ts b/src/Aldap.ts deleted file mode 100644 index cc7672c..0000000 --- a/src/Aldap.ts +++ /dev/null @@ -1,101 +0,0 @@ -import Application from "wms-core/Application"; -import {Type} from "wms-core/Utils"; -import Migration from "wms-core/db/Migration"; -import ExpressAppComponent from "wms-core/components/ExpressAppComponent"; -import NunjucksComponent from "wms-core/components/NunjucksComponent"; -import MysqlComponent from "wms-core/components/MysqlComponent"; -import LogRequestsComponent from "wms-core/components/LogRequestsComponent"; -import RedisComponent from "wms-core/components/RedisComponent"; -import ServeStaticDirectoryComponent from "wms-core/components/ServeStaticDirectoryComponent"; -import MaintenanceComponent from "wms-core/components/MaintenanceComponent"; -import MailComponent from "wms-core/components/MailComponent"; -import SessionComponent from "wms-core/components/SessionComponent"; -import RedirectBackComponent from "wms-core/components/RedirectBackComponent"; -import FormHelperComponent from "wms-core/components/FormHelperComponent"; -import CsrfProtectionComponent from "wms-core/components/CsrfProtectionComponent"; -import WebSocketServerComponent from "wms-core/components/WebSocketServerComponent"; -import HomeController from "./controllers/HomeController"; -import AuthController from "./controllers/AuthController"; -import AuthComponent from "wms-core/auth/AuthComponent"; -import AuthGuard from "wms-core/auth/AuthGuard"; -import {PasswordAuthProof} from "./models/UserPassword"; -import {MIGRATIONS} from "./migrations"; -import LDAPServerComponent from "./LDAPServerComponent"; -import PreLaunchWall from "./controllers/PreLaunchWall"; -import AutoUpdateComponent from "wms-core/components/AutoUpdateComponent"; - -export default class Aldap extends Application { - private readonly port: number; - - constructor(port: number) { - super(require('../package.json').version); - this.port = port; - } - - protected getMigrations(): Type[] { - return MIGRATIONS; - } - - protected async init(): Promise { - this.registerComponents(); - this.registerWebSocketListeners(); - this.registerControllers(); - } - - private registerComponents() { - const redisComponent = new RedisComponent(); - const mysqlComponent = new MysqlComponent(); - - const expressAppComponent = new ExpressAppComponent(this.port); - this.use(expressAppComponent); - this.use(new NunjucksComponent()); - this.use(new LogRequestsComponent()); - - // Static files - this.use(new ServeStaticDirectoryComponent('public')); - this.use(new ServeStaticDirectoryComponent('node_modules/feather-icons/dist', '/icons')); - - // Maintenance - this.use(new MaintenanceComponent(this, () => { - return redisComponent.canServe() && mysqlComponent.canServe(); - })); - this.use(new AutoUpdateComponent()); - - // Services - this.use(mysqlComponent); - this.use(new MailComponent()); - - // Session - this.use(redisComponent); - this.use(new SessionComponent(redisComponent)); - - // Utils - this.use(new RedirectBackComponent()); - this.use(new FormHelperComponent()); - - // Middlewares - this.use(new CsrfProtectionComponent()); - - // Auth - this.use(new AuthComponent(new class extends AuthGuard { - public async getProofForSession(session: Express.Session): Promise { - return PasswordAuthProof.getProofForSession(session); - } - })); - - // WebSocket server - this.use(new WebSocketServerComponent(this, expressAppComponent, redisComponent)); - - // LDAP server - this.use(new LDAPServerComponent()); - } - - private registerWebSocketListeners() { - } - - private registerControllers() { - this.use(new PreLaunchWall()); - this.use(new HomeController()); - this.use(new AuthController()); - } -} \ No newline at end of file diff --git a/src/App.ts b/src/App.ts index 713f51b..c3dfbae 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,8 +1,6 @@ import Application from "wms-core/Application"; import {Type} from "wms-core/Utils"; import Migration from "wms-core/db/Migration"; -import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; -import CreateLogsTable from "wms-core/migrations/CreateLogsTable"; import ExpressAppComponent from "wms-core/components/ExpressAppComponent"; import NunjucksComponent from "wms-core/components/NunjucksComponent"; import MysqlComponent from "wms-core/components/MysqlComponent"; @@ -12,15 +10,30 @@ import ServeStaticDirectoryComponent from "wms-core/components/ServeStaticDirect import MaintenanceComponent from "wms-core/components/MaintenanceComponent"; import MailComponent from "wms-core/components/MailComponent"; import SessionComponent from "wms-core/components/SessionComponent"; -import RedirectBackComponent from "wms-core/components/RedirectBackComponent"; import FormHelperComponent from "wms-core/components/FormHelperComponent"; import CsrfProtectionComponent from "wms-core/components/CsrfProtectionComponent"; import WebSocketServerComponent from "wms-core/components/WebSocketServerComponent"; import HomeController from "./controllers/HomeController"; +import AuthController from "./controllers/AuthController"; +import AuthComponent from "wms-core/auth/AuthComponent"; +import AuthGuard from "wms-core/auth/AuthGuard"; +import {PasswordAuthProof} from "./models/UserPassword"; +import LDAPServerComponent from "./LDAPServerComponent"; import AutoUpdateComponent from "wms-core/components/AutoUpdateComponent"; +import AccountController from "./controllers/AccountController"; +import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; +import CreateLogsTable from "wms-core/migrations/CreateLogsTable"; +import CreateUsersAndUserEmailsTable from "wms-core/auth/migrations/CreateUsersAndUserEmailsTable"; +import CreateUserPasswordsTable from "./migrations/CreateUserPasswordsTable"; +import CreateUsernamesTable from "./migrations/CreateUsernamesTable"; +import CreateMagicLinksTable from "wms-core/auth/migrations/CreateMagicLinksTable"; +import MailController from "wms-core/auth/MailController"; +import MagicLinkController from "./controllers/MagicLinkController"; +import MagicLinkWebSocketListener from "wms-core/auth/magic_link/MagicLinkWebSocketListener"; export default class App extends Application { private readonly port: number; + private magicLinkWebSocketListener?: MagicLinkWebSocketListener; constructor(port: number) { super(require('../package.json').version); @@ -31,6 +44,10 @@ export default class App extends Application { return [ CreateMigrationsTable, CreateLogsTable, + CreateUsersAndUserEmailsTable, + CreateUserPasswordsTable, + CreateUsernamesTable, + CreateMagicLinksTable, ]; } @@ -68,20 +85,42 @@ export default class App extends Application { this.use(new SessionComponent(redisComponent)); // Utils - this.use(new RedirectBackComponent()); this.use(new FormHelperComponent()); // Middlewares this.use(new CsrfProtectionComponent()); + // Auth + this.use(new AuthComponent(new class extends AuthGuard { + public async getProofForSession(session: Express.Session): Promise { + return PasswordAuthProof.getProofForSession(session); + } + })); + // WebSocket server this.use(new WebSocketServerComponent(this, expressAppComponent, redisComponent)); + + // LDAP server + this.use(new LDAPServerComponent()); } private registerWebSocketListeners() { + this.magicLinkWebSocketListener = new MagicLinkWebSocketListener(); + this.use(this.magicLinkWebSocketListener); } private registerControllers() { + // Priority routes / interrupting middlewares + this.use(new AuthController()); + this.use(new AccountController()); + this.use(new MagicLinkController(this.magicLinkWebSocketListener!)) + + // Core functionality + this.use(new MailController()); + + // Other functionnality + + // Semi-static routes this.use(new HomeController()); } } \ No newline at end of file diff --git a/src/LDAPServerComponent.ts b/src/LDAPServerComponent.ts index 3026faf..db042da 100644 --- a/src/LDAPServerComponent.ts +++ b/src/LDAPServerComponent.ts @@ -1,27 +1,18 @@ import ApplicationComponent from "wms-core/ApplicationComponent"; -import {Express, Router} from "express"; -import ldap, {InsufficientAccessRightsError, InvalidCredentialsError, Server} from "ldapjs"; +import {Express} from "express"; +import ldap, {InvalidCredentialsError, Server} from "ldapjs"; import Logger from "wms-core/Logger"; import Username from "./models/Username"; -import UserEmail from "wms-core/auth/models/UserEmail"; import {PasswordAuthProof} from "./models/UserPassword"; import Throttler from "wms-core/Throttler"; export default class LDAPServerComponent extends ApplicationComponent { private server?: Server; - public async start(app: Express, router: Router): Promise { + public async start(app: Express): Promise { this.server = ldap.createServer({ log: console }); - let authorize = (req: any, res: any, next: any) => { - Logger.debug(req); - - if (!req.connection.ldap.bindDN.equals('cn=root')) - return next(new InsufficientAccessRightsError()); - - return next(); - }; this.server.bind('ou=users,dc=toot,dc=party', async (req: any, res: any, next: any) => { const rdns = req.dn.toString().split(', ').map((rdn: string) => rdn.split('=')); let username; @@ -46,7 +37,7 @@ export default class LDAPServerComponent extends ApplicationComponent { const user = await Username.getUserFromUsername(username); if (user) { - const email = await UserEmail.getMainFromUser(user.id!); + const email = await user.mainEmail.get(); if (email) { const authProof = new PasswordAuthProof(email.email!); if (await authProof.authorize(req.credentials)) { @@ -68,7 +59,4 @@ export default class LDAPServerComponent extends ApplicationComponent { Logger.info(`LDAP server listening on ${this.server!.url}`); }); } - - public async stop(): Promise { - } } \ No newline at end of file diff --git a/src/controllers/AccountController.ts b/src/controllers/AccountController.ts new file mode 100644 index 0000000..bf2373e --- /dev/null +++ b/src/controllers/AccountController.ts @@ -0,0 +1,21 @@ +import Controller from "wms-core/Controller"; +import {REQUIRE_AUTH_MIDDLEWARE} from "wms-core/auth/AuthComponent"; +import {NextFunction, Request, Response} from "express"; + +export default class AccountController extends Controller { + routes(): void { + this.get('/account', this.getAccount, 'account', REQUIRE_AUTH_MIDDLEWARE); + this.post('/add-recovery-email', this.addRecoveryEmail, 'add-recovery-email', REQUIRE_AUTH_MIDDLEWARE); + } + + protected async getAccount(req: Request, res: Response, next: NextFunction): Promise { + res.render('account', { + emails: await req.models.user!.emails.get(), + }); + } + + protected async addRecoveryEmail(req: Request, res: Response, next: NextFunction): Promise { + req.flash('warn', 'Not implemented'); + res.redirectBack(); + } +} \ No newline at end of file diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index fc27cae..ee32630 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -1,26 +1,28 @@ import Controller from "wms-core/Controller"; import {REQUIRE_AUTH_MIDDLEWARE, REQUIRE_GUEST_MIDDLEWARE} from "wms-core/auth/AuthComponent"; -import {Request, Response} from "express"; +import {NextFunction, Request, Response} from "express"; import Validator from "wms-core/db/Validator"; import {EMAIL_REGEX} from "wms-core/db/Model"; import {PasswordAuthProof} from "../models/UserPassword"; import UserEmail from "wms-core/auth/models/UserEmail"; import Username, {USERNAME_REGEXP} from "../models/Username"; +import _AuthController from "wms-core/auth/AuthController"; +import {ServerError} from "wms-core/HttpError"; -export default class AuthController extends Controller { +export default class AuthController extends _AuthController { routes(): void { this.get('/login', this.getLogin, 'login', REQUIRE_GUEST_MIDDLEWARE); this.post('/login', this.postLogin, 'login', REQUIRE_GUEST_MIDDLEWARE); this.get('/register', this.getRegister, 'register', REQUIRE_GUEST_MIDDLEWARE); this.post('/register', this.postRegister, 'register', REQUIRE_GUEST_MIDDLEWARE); - this.get('/logout', this.getLogout, 'logout', REQUIRE_AUTH_MIDDLEWARE); + this.post('/logout', this.postLogout, 'logout', REQUIRE_AUTH_MIDDLEWARE); } - private async getLogin(req: Request, res: Response): Promise { + protected async getLogin(req: Request, res: Response): Promise { res.render('login'); } - private async postLogin(req: Request, res: Response): Promise { + protected async postLogin(req: Request, res: Response): Promise { await this.validate({ email: new Validator().defined().regexp(EMAIL_REGEX), password: new Validator().acceptUndefined(), @@ -41,11 +43,11 @@ export default class AuthController extends Controller { res.redirect(Controller.route('home')); } - private async getRegister(req: Request, res: Response): Promise { + protected async getRegister(req: Request, res: Response): Promise { res.render('register'); } - private async postRegister(req: Request, res: Response): Promise { + protected async postRegister(req: Request, res: Response): Promise { const validationMap: any = { username: new Validator().defined().between(3, 64).regexp(USERNAME_REGEXP).unique(Username), password: new Validator().defined().minLength(8), @@ -96,8 +98,11 @@ export default class AuthController extends Controller { res.redirect(Controller.route('home')); } - private async getLogout(req: Request, res: Response): Promise { - await req.authGuard.logout(req.session!); - res.redirect(Controller.route('home')); + protected async getCheckAuth(req: Request, res: Response, next: NextFunction): Promise { + throw new ServerError('Not implemented.'); + } + + protected async postAuth(req: Request, res: Response, next: NextFunction): Promise { + throw new ServerError('Not implemented.'); } } \ No newline at end of file diff --git a/src/controllers/MagicLinkActionType.ts b/src/controllers/MagicLinkActionType.ts new file mode 100644 index 0000000..eda96d5 --- /dev/null +++ b/src/controllers/MagicLinkActionType.ts @@ -0,0 +1,3 @@ +export enum MagicLinkActionType { + ADD_RECOVERY_EMAIL = 'Add a recovery email', +} \ No newline at end of file diff --git a/src/controllers/MagicLinkController.ts b/src/controllers/MagicLinkController.ts new file mode 100644 index 0000000..8035f9e --- /dev/null +++ b/src/controllers/MagicLinkController.ts @@ -0,0 +1,35 @@ +import _MagicLinkController from "wms-core/auth/magic_link/MagicLinkController"; +import MagicLink from "wms-core/auth/models/MagicLink"; +import {Request, Response} from "express"; +import MagicLinkWebSocketListener from "wms-core/auth/magic_link/MagicLinkWebSocketListener"; +import {MagicLinkActionType} from "./MagicLinkActionType"; +import Controller from "wms-core/Controller"; +import {BadOwnerMagicLink} from "wms-core/auth/magic_link/MagicLinkAuthController"; +import UserEmail from "wms-core/auth/models/UserEmail"; + +export default class MagicLinkController extends _MagicLinkController { + constructor(magicLinkWebSocketListener: MagicLinkWebSocketListener) { + super(magicLinkWebSocketListener); + } + + protected async performAction(magicLink: MagicLink, req: Request, res: Response): Promise { + switch (magicLink.getActionType()) { + case MagicLinkActionType.ADD_RECOVERY_EMAIL: + if (magicLink.getSessionID() !== req.sessionID!) throw new BadOwnerMagicLink(); + + const user = await req.authGuard.getUserForSession(req.session!); + if (!user || !(await magicLink.isOwnedBy(user.id!))) throw new BadOwnerMagicLink(); + + let userEmail; + await (userEmail = new UserEmail({ + user_id: user.id, + email: await magicLink.getEmail(), + main: false, + })).save(); + + req.flash('success', `Recovery email ${userEmail.email} successfully added.`); + res.redirect(Controller.route('home')); + break; + } + } +} \ No newline at end of file diff --git a/src/controllers/PreLaunchWall.ts b/src/controllers/PreLaunchWall.ts deleted file mode 100644 index 9fd2274..0000000 --- a/src/controllers/PreLaunchWall.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Controller from "wms-core/Controller"; -import {Request, RequestHandler, Response} from "express"; -import {ForbiddenHttpError} from "wms-core/HttpError"; -import Validator from "wms-core/db/Validator"; -import argon2 from "argon2"; -import config from "config"; - -export default class PreLaunchWall extends Controller { - public getGlobalHandlers(): RequestHandler[] { - return [ - (req, res, next) => { - if (!req.session) throw new ForbiddenHttpError('page', req.url); - - if (!req.session.authorized) { - const route = Controller.route('prelaunch-wall'); - if (req.url !== route) { - res.redirect(route); - return; - } - } - - next(); - } - ]; - } - - routes(): void { - this.get('/prelaunch-wall', this.getWall, 'prelaunch-wall'); - this.post('/prelaunch-wall', this.postWall, 'prelaunch-wall'); - } - - private async getWall(req: Request, res: Response) { - res.render('prelaunch-wall'); - } - - private async postWall(req: Request, res: Response) { - await this.validate({ - password: new Validator().defined(), - }, req.body); - - if (await argon2.verify(config.get('prelaunch-password'), req.body.password)) { - req.session!.authorized = true; - req.flash('success', 'Authentication success!'); - res.redirect(Controller.route('home')); - return; - } - - req.flash('error', 'Invalid password.'); - res.redirectBack(); - } -} \ No newline at end of file diff --git a/src/migrations.ts b/src/migrations.ts deleted file mode 100644 index a911950..0000000 --- a/src/migrations.ts +++ /dev/null @@ -1,13 +0,0 @@ -import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; -import CreateLogsTable from "wms-core/migrations/CreateLogsTable"; -import CreateUsersAndUserEmailsTable from "wms-core/auth/migrations/CreateUsersAndUserEmailsTable"; -import CreateUserPasswordsTable from "./migrations/CreateUserPasswordsTable"; -import CreateUsernamesTable from "./migrations/CreateUsernamesTable"; - -export const MIGRATIONS = [ - CreateMigrationsTable, - CreateLogsTable, - CreateUsersAndUserEmailsTable, - CreateUserPasswordsTable, - CreateUsernamesTable, -]; \ No newline at end of file diff --git a/src/models/UserPassword.ts b/src/models/UserPassword.ts index 20f122a..d03b58e 100644 --- a/src/models/UserPassword.ts +++ b/src/models/UserPassword.ts @@ -4,22 +4,26 @@ import User from "wms-core/auth/models/User"; import argon2 from "argon2"; import AuthProof from "wms-core/auth/AuthProof"; import {UserAlreadyExistsAuthError} from "wms-core/auth/AuthGuard"; +import UserEmail from "wms-core/auth/models/UserEmail"; export default class UserPassword extends Model { public static async getByEmail(email: string): Promise { - const user = await User.fromEmail(email); - if (!user) return null; - - const result = await this.models(this.select().where('user_id', user.id).first()); - return result && result.length > 0 ? result[0] : null; + const userEmail = await UserEmail.select('user_id') + .where('email', email) + .first(); + return userEmail ? await UserPassword.select().where('user_id', userEmail.user_id).first() : null; } - private user_id?: number; - private password?: string; + private user_id!: number; + private password!: string; - protected defineProperties(): void { - this.defineProperty('user_id', new Validator().defined().unique(this).exists(User, 'id')); - this.defineProperty('password', new Validator().defined()); + public constructor(props: any) { + super(props); + } + + protected init(): void { + this.addProperty('user_id', new Validator().defined().unique(this).exists(User, 'id')); + this.addProperty('password', new Validator().defined()); } public async setUser(userID: number): Promise { @@ -67,7 +71,11 @@ export class PasswordAuthProof implements AuthProof { } public async getUser(): Promise { - return await User.fromEmail(await this.getEmail()); + const userEmail = await UserEmail.select() + .where('email', await this.getEmail()) + .with('user') + .first(); + return userEmail ? await userEmail.user.get() : null; } public async isAuthorized(): Promise { diff --git a/src/models/Username.ts b/src/models/Username.ts index ffbcb6d..2e20e8e 100644 --- a/src/models/Username.ts +++ b/src/models/Username.ts @@ -1,32 +1,38 @@ import Model from "wms-core/db/Model"; import Validator from "wms-core/db/Validator"; import User from "wms-core/auth/models/User"; +import {OneModelRelation} from "wms-core/db/ModelRelation"; export const USERNAME_REGEXP = /^[0-9a-z_-]+$/; export default class Username extends Model { public static async fromUser(userID: number): Promise { - const models = await this.models(this.select().where('user_id', userID)); - return models && models.length > 0 ? models[0] : null; + return await this.select().where('user_id', userID).first(); } public static async getUserFromUsername(username: string): Promise { - const models = await this.models(this.select().where('username', username.toLowerCase())); - if (!models || models.length === 0) return null; - return await User.getById(models[0].user_id!); + let model = await this.select() + .with('user') + .where('username', username.toLowerCase()) + .first(); + return model ? await model.user.get() : null; } - private user_id?: number; - public username?: string; + private readonly user_id!: number; + public readonly user: OneModelRelation = new OneModelRelation(this, User, { + localKey: 'user_id', + foreignKey: 'id' + }); + public username!: string; constructor(data: any) { super(data); } - protected defineProperties(): void { - this.defineProperty('user_id', new Validator().defined().exists(User, 'id').unique(this)); - this.defineProperty('username', new Validator().defined().between(3, 64).regexp(USERNAME_REGEXP).unique(this)); + protected init(): void { + this.addProperty('user_id', new Validator().defined().exists(User, 'id').unique(this)); + this.addProperty('username', new Validator().defined().between(3, 64).regexp(USERNAME_REGEXP).unique(this)); } } \ No newline at end of file diff --git a/views/account.njk b/views/account.njk new file mode 100644 index 0000000..7ab5edb --- /dev/null +++ b/views/account.njk @@ -0,0 +1,31 @@ +{% extends 'layouts/base.njk' %} + +{% set title = 'ALDAP - Welcome to the toot.party auth center!' %} + +{% block body %} +
+
+

My account

+ +

Name: {{ user.name }}

+ +
+

Email addresses

+ + {% for email in emails %} + {% if email.main %} +

Main email: {{ email.email }}

+ {% else %} +

Recovery email: {{ email.email }}

+ {% endif %} + {% endfor %} + +
+ {{ macros.field(_locals, 'email', 'email', null, 'Choose a safe email address', 'An email we can use to identify you in case you lose access to your account', 'required') }} + + +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/views/layouts/base.njk b/views/layouts/base.njk index 131cda7..b9494ca 100644 --- a/views/layouts/base.njk +++ b/views/layouts/base.njk @@ -16,15 +16,21 @@ {% endblock %} diff --git a/views/login.njk b/views/login.njk index 1b45bac..f6a90d1 100644 --- a/views/login.njk +++ b/views/login.njk @@ -11,7 +11,7 @@ {{ macros.field(_locals, 'email', 'email', null, 'Your email address', null, 'required') }} {{ macros.field(_locals, 'password', 'password', null, 'Your password', null, 'required') }} - + {{ macros.csrf(getCSRFToken) }} diff --git a/views/prelaunch-wall.njk b/views/prelaunch-wall.njk deleted file mode 100644 index 99e8741..0000000 --- a/views/prelaunch-wall.njk +++ /dev/null @@ -1,19 +0,0 @@ -{% extends 'layouts/base.njk' %} - -{% set title = 'ALDAP - Early access' %} - -{% block body %} -
-
-

{{ title }}

- -
- {{ macros.field(_locals, 'password', 'password', null, 'Enter password') }} - - - - {{ macros.csrf(getCSRFToken) }} -
-
-
-{% endblock %} \ No newline at end of file diff --git a/views/register.njk b/views/register.njk index f62501d..5079713 100644 --- a/views/register.njk +++ b/views/register.njk @@ -26,7 +26,7 @@ {{ macros.field(_locals, 'select', 'domain', null, 'Choose your domain', null, 'disabled', ['toot.party']) }} {{ macros.fieldError(_locals, 'email') }} -
This cannot be changed later.
+
You won't be able to change this again.
@@ -42,7 +42,7 @@ {{ macros.field(_locals, 'checkbox', 'terms', null, 'I accept the terms of services', null, 'required') }} - + {{ macros.csrf(getCSRFToken) }}