[task-207] eigentlich schon fertsch :)
This commit is contained in:
parent
8a0e1b1843
commit
3b4a7809dc
14 changed files with 395 additions and 133 deletions
|
@ -56,13 +56,6 @@
|
||||||
"login_url": null
|
"login_url": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admins": [
|
|
||||||
{
|
|
||||||
"name": "admin",
|
|
||||||
"password_image": "$2b$12$xOa6iWPOMjiwJ3oIOZWDGu/w2Ca/eKLHWE7aDItkNsP/79nJk065i",
|
|
||||||
"email_address": "espe-admin@example.org"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"output": {
|
"output": {
|
||||||
"authelia": "/tmp/authelia-users.yml"
|
"authelia": "/tmp/authelia-users.yml"
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,15 +64,15 @@ namespace _espe.api
|
||||||
return Promise.reject(new Error("impossible"));
|
return Promise.reject(new Error("impossible"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const admin : (null | _espe.service.admin.type_value) = await _espe.service.admin.login(input.name, input.password);
|
const admin_entry : (null | _espe.service.admin.type_value) = await _espe.service.admin.login(input.name, input.password);
|
||||||
if (admin === null) {
|
if (admin_entry === null) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
"status_code": 403,
|
"status_code": 403,
|
||||||
"data": null,
|
"data": null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const session_key : string = await lib_plankton.session.begin(admin.name);
|
const session_key : string = await lib_plankton.session.begin(admin_entry.object.name);
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
"status_code": 201,
|
"status_code": 201,
|
||||||
"data": session_key,
|
"data": session_key,
|
||||||
|
|
|
@ -169,14 +169,6 @@ namespace _espe.conf
|
||||||
login_url : (null | string);
|
login_url : (null | string);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// TODO: evtl. in Datenbank verlagern
|
|
||||||
admins : Array<
|
|
||||||
{
|
|
||||||
name : string;
|
|
||||||
password_image : string;
|
|
||||||
email_address : (null | string);
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
outputs : Array<
|
outputs : Array<
|
||||||
{
|
{
|
||||||
kind : "authelia_file";
|
kind : "authelia_file";
|
||||||
|
@ -221,7 +213,7 @@ namespace _espe.conf
|
||||||
conf_raw : any
|
conf_raw : any
|
||||||
) : void
|
) : void
|
||||||
{
|
{
|
||||||
const version : int = (conf_raw["version"] ?? 4);
|
const version : int = (conf_raw["version"] ?? 5);
|
||||||
_data = {
|
_data = {
|
||||||
"general": (
|
"general": (
|
||||||
((node_general) => ({
|
((node_general) => ({
|
||||||
|
@ -245,7 +237,8 @@ namespace _espe.conf
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
case 4: {
|
case 4:
|
||||||
|
case 5: {
|
||||||
const node_log = (
|
const node_log = (
|
||||||
conf_raw["log"]
|
conf_raw["log"]
|
||||||
??
|
??
|
||||||
|
@ -286,7 +279,8 @@ namespace _espe.conf
|
||||||
return "::";
|
return "::";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4:
|
||||||
|
case 5: {
|
||||||
return (node_server["host"] ?? "::");
|
return (node_server["host"] ?? "::");
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -398,7 +392,7 @@ namespace _espe.conf
|
||||||
"remark": (node_settings_summon_email["remark"] ?? null),
|
"remark": (node_settings_summon_email["remark"] ?? null),
|
||||||
})) (node_settings["summon_email"] ?? {})
|
})) (node_settings["summon_email"] ?? {})
|
||||||
),
|
),
|
||||||
"password_policy": (
|
"password_policy": (
|
||||||
((node_settings_password_policy) => ({
|
((node_settings_password_policy) => ({
|
||||||
"minimum_length": (
|
"minimum_length": (
|
||||||
("minimum_length" in node_settings_password_policy)
|
("minimum_length" in node_settings_password_policy)
|
||||||
|
@ -434,7 +428,6 @@ namespace _espe.conf
|
||||||
),
|
),
|
||||||
})) (conf_raw["settings"] ?? {})
|
})) (conf_raw["settings"] ?? {})
|
||||||
),
|
),
|
||||||
"admins": (conf_raw["admins"] ?? []),
|
|
||||||
"outputs": (() => {
|
"outputs": (() => {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"help.args.action.options.email_test": "eine Test-E-Mail senden",
|
"help.args.action.options.email_test": "eine Test-E-Mail senden",
|
||||||
"help.args.action.options.expose_conf": "Vollständige Konfiguration ausgeben",
|
"help.args.action.options.expose_conf": "Vollständige Konfiguration ausgeben",
|
||||||
"help.args.action.options.password_image": "Passwort-Abbild errechnen und auf Standard-Ausgabe schreiben",
|
"help.args.action.options.password_image": "Passwort-Abbild errechnen und auf Standard-Ausgabe schreiben",
|
||||||
|
"help.args.action.options.admin_add": "Einen Administrator-Zugang anlegen",
|
||||||
"help.args.action.options.export_authelia": "Export der Nutzer-Datenbank im Authelia-user-Datei-Format auf Standard-Ausgabe schreiben",
|
"help.args.action.options.export_authelia": "Export der Nutzer-Datenbank im Authelia-user-Datei-Format auf Standard-Ausgabe schreiben",
|
||||||
"help.args.action.options.help": "Diese Hilfe auf Standard-Ausgabe schreiben",
|
"help.args.action.options.help": "Diese Hilfe auf Standard-Ausgabe schreiben",
|
||||||
"help.args.conf_path.description": "Pfad zur Konfigurations-Datei",
|
"help.args.conf_path.description": "Pfad zur Konfigurations-Datei",
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"help.args.action.options.email_test": "send a test e-mail",
|
"help.args.action.options.email_test": "send a test e-mail",
|
||||||
"help.args.action.options.expose_conf": "write complete configuration to stdout",
|
"help.args.action.options.expose_conf": "write complete configuration to stdout",
|
||||||
"help.args.action.options.password_image": "compute password image and write to stdout",
|
"help.args.action.options.password_image": "compute password image and write to stdout",
|
||||||
|
"help.args.action.options.admin_add": "create an admin account",
|
||||||
"help.args.action.options.export_authelia": "export user database in Authelia user file format and write to stdout",
|
"help.args.action.options.export_authelia": "export user database in Authelia user file format and write to stdout",
|
||||||
"help.args.action.options.help": "write this help to stdout",
|
"help.args.action.options.help": "write this help to stdout",
|
||||||
"help.args.conf_path.description": "path to configuration file",
|
"help.args.conf_path.description": "path to configuration file",
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace _espe.database
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
const _compatible_revisions : Array<string> = [
|
const _compatible_revisions : Array<string> = [
|
||||||
"r5",
|
"r6",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -248,25 +248,4 @@ namespace _espe.helpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export async function notify_admins(
|
|
||||||
subject : string,
|
|
||||||
body : string
|
|
||||||
) : Promise<void>
|
|
||||||
{
|
|
||||||
await _espe.helpers.email_send(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
_espe.conf.get().admins
|
|
||||||
.map(admin => admin.email_address)
|
|
||||||
.filter(x => (x !== null))
|
|
||||||
) as Array<string>
|
|
||||||
),
|
|
||||||
subject,
|
|
||||||
body
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,10 @@ async function main(
|
||||||
"name": "password-image",
|
"name": "password-image",
|
||||||
"description": lib_plankton.translate.get("help.args.action.options.password_image")
|
"description": lib_plankton.translate.get("help.args.action.options.password_image")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "admin-add",
|
||||||
|
"description": lib_plankton.translate.get("help.args.action.options.admin_add")
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "export-authelia",
|
"name": "export-authelia",
|
||||||
"description": lib_plankton.translate.get("help.args.action.options.export_authelia")
|
"description": lib_plankton.translate.get("help.args.action.options.export_authelia")
|
||||||
|
@ -153,6 +157,15 @@ async function main(
|
||||||
"name": "arg2",
|
"name": "arg2",
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
}),
|
}),
|
||||||
|
"arg3": lib_plankton.args.class_argument.positional({
|
||||||
|
"index": 3,
|
||||||
|
"type": lib_plankton.args.enum_type.string,
|
||||||
|
"mode": lib_plankton.args.enum_mode.replace,
|
||||||
|
"default": null,
|
||||||
|
// "info": null,
|
||||||
|
"name": "arg3",
|
||||||
|
"hidden": true,
|
||||||
|
}),
|
||||||
"conf_path": lib_plankton.args.class_argument.volatile({
|
"conf_path": lib_plankton.args.class_argument.volatile({
|
||||||
"indicators_long": ["conf_path"],
|
"indicators_long": ["conf_path"],
|
||||||
"indicators_short": ["c"],
|
"indicators_short": ["c"],
|
||||||
|
@ -257,13 +270,7 @@ async function main(
|
||||||
(
|
(
|
||||||
(args["arg1"] !== null)
|
(args["arg1"] !== null)
|
||||||
? [args["arg1"]]
|
? [args["arg1"]]
|
||||||
: (
|
: (await _espe.service.admin.email_addresses())
|
||||||
(
|
|
||||||
_espe.conf.get().admins
|
|
||||||
.map(admin => admin.email_address)
|
|
||||||
.filter(x => (x !== null))
|
|
||||||
) as Array<string>
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
lib_plankton.string.coin(
|
lib_plankton.string.coin(
|
||||||
"{{head}} | Test",
|
"{{head}} | Test",
|
||||||
|
@ -275,6 +282,23 @@ async function main(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "admin-add": {
|
||||||
|
const name : (null | string) = args["arg1"];
|
||||||
|
const email_address : (null | string) = args["arg2"];
|
||||||
|
const password : (null | string) = args["arg3"];
|
||||||
|
if ((name === null) || (email_address === null) || (password === null)) {
|
||||||
|
throw (new Error("SYNTAX: admin-add <name> <email-address> <password>"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const admin_id : _espe.type.admin_id = await _espe.service.admin.add(
|
||||||
|
name,
|
||||||
|
email_address,
|
||||||
|
password
|
||||||
|
);
|
||||||
|
process.stdout.write(admin_id.toFixed(0) + "\n")
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "serve": {
|
case "serve": {
|
||||||
// prepare database
|
// prepare database
|
||||||
await _espe.database.check();
|
await _espe.database.check();
|
||||||
|
|
171
source/repositories/admin.ts
Normal file
171
source/repositories/admin.ts
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
Espe | Ein schlichtes Werkzeug zur Mitglieder-Verwaltung | Backend
|
||||||
|
Copyright (C) 2024 Christian Fraß
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with this program. If not, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace _espe.repository.admin
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
var _store : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
lib_plankton.storage.type_store<
|
||||||
|
_espe.type.admin_id,
|
||||||
|
Record<string, any>,
|
||||||
|
{},
|
||||||
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
|
Record<string, any>
|
||||||
|
>
|
||||||
|
) = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function get_store(
|
||||||
|
) : lib_plankton.storage.type_store<
|
||||||
|
_espe.type.admin_id,
|
||||||
|
Record<string, any>,
|
||||||
|
{},
|
||||||
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
|
Record<string, any>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if (_store === null) {
|
||||||
|
_store = lib_plankton.storage.sql_table_autokey_store(
|
||||||
|
{
|
||||||
|
"database_implementation": _espe.helpers.database_implementation(),
|
||||||
|
"table_name": "admins",
|
||||||
|
"key_name": "id",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
return _store;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_dispersal = Record<string, any>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function encode(
|
||||||
|
object : _espe.type.admin_object
|
||||||
|
) : type_dispersal
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"name": object.name,
|
||||||
|
"email_address": object.email_address,
|
||||||
|
"password_image": object.password_image,
|
||||||
|
"password_fail_count": object.password_fail_count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function decode(
|
||||||
|
dispersal : type_dispersal
|
||||||
|
) : _espe.type.admin_object
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"name": dispersal["name"],
|
||||||
|
"email_address": dispersal["email_address"],
|
||||||
|
"password_image": dispersal["password_image"],
|
||||||
|
"password_fail_count": dispersal["password_fail_count"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function list(
|
||||||
|
search_term : (null | string)
|
||||||
|
) : Promise<
|
||||||
|
Array<
|
||||||
|
{
|
||||||
|
id : _espe.type.admin_id;
|
||||||
|
preview : _espe.type.admin_object;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
(await get_store().search(null))
|
||||||
|
.filter(
|
||||||
|
({"key": key, "preview": preview}) => (
|
||||||
|
(
|
||||||
|
(search_term === null)
|
||||||
|
||
|
||||||
|
(search_term.length <= 1)
|
||||||
|
)
|
||||||
|
?
|
||||||
|
true
|
||||||
|
:
|
||||||
|
(preview["name"].toLowerCase().includes(search_term.toLowerCase()))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
({"key": key, "preview": preview}) => ({
|
||||||
|
"id": key,
|
||||||
|
"preview": decode(preview),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function read(
|
||||||
|
id : _espe.type.admin_id
|
||||||
|
) : Promise<_espe.type.admin_object>
|
||||||
|
{
|
||||||
|
const row : Record<string, any> = await get_store().read(id);
|
||||||
|
const dispersal : type_dispersal = row;
|
||||||
|
|
||||||
|
return decode(dispersal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function create(
|
||||||
|
value : _espe.type.admin_object
|
||||||
|
) : Promise<_espe.type.admin_id>
|
||||||
|
{
|
||||||
|
const dispersal : type_dispersal = encode(value);
|
||||||
|
|
||||||
|
return get_store().create(dispersal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function update(
|
||||||
|
id : _espe.type.admin_id,
|
||||||
|
value : _espe.type.admin_object
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const dispersal : type_dispersal = encode(value);
|
||||||
|
|
||||||
|
return get_store().update(id, dispersal);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
Espe | Ein schlichtes Werkzeug zur Mitglieder-Verwaltung | Backend
|
|
||||||
Copyright (C) 2024 Christian Fraß
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
|
||||||
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
||||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along with this program. If not, see
|
|
||||||
<https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace _espe.service.admin
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export type type_value = {
|
|
||||||
name : string;
|
|
||||||
password_image : string;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
function get(
|
|
||||||
name : string
|
|
||||||
) : (null | type_value)
|
|
||||||
{
|
|
||||||
const admins : Array<{name : string; password_image : string;}> = _espe.conf.get().admins;
|
|
||||||
const entry : (undefined | {name : string; password_image : string;}) = admins.find(entry_ => (entry_.name === name));
|
|
||||||
return (
|
|
||||||
(entry === undefined)
|
|
||||||
? null
|
|
||||||
: {
|
|
||||||
"name": entry.name,
|
|
||||||
"password_image": entry.password_image,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
async function check_password(
|
|
||||||
value : type_value,
|
|
||||||
password_given : string
|
|
||||||
) : Promise<boolean>
|
|
||||||
{
|
|
||||||
return _espe.helpers.bcrypt_compare(value.password_image, password_given);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export async function login(
|
|
||||||
name : string,
|
|
||||||
password : string
|
|
||||||
) : Promise<(null | type_value)>
|
|
||||||
{
|
|
||||||
const value : (null | type_value) = get(name);
|
|
||||||
if (value === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (! (await check_password(value, password))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
163
source/services/admin.ts
Normal file
163
source/services/admin.ts
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
Espe | Ein schlichtes Werkzeug zur Mitglieder-Verwaltung | Backend
|
||||||
|
Copyright (C) 2024 Christian Fraß
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with this program. If not, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace _espe.service.admin
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type type_value = {
|
||||||
|
id : _espe.type.admin_id;
|
||||||
|
object : _espe.type.admin_object;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function add(
|
||||||
|
name : string,
|
||||||
|
email_address : (null | string),
|
||||||
|
password : string
|
||||||
|
) : Promise<_espe.type.admin_id>
|
||||||
|
{
|
||||||
|
const admin_object : _espe.type.admin_object = {
|
||||||
|
"name": name,
|
||||||
|
"email_address": email_address,
|
||||||
|
"password_image": (await _espe.helpers.bcrypt_compute(password)),
|
||||||
|
"password_fail_count": 0,
|
||||||
|
};
|
||||||
|
return _espe.repository.admin.create(admin_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async function get(
|
||||||
|
name : string
|
||||||
|
) : Promise<(null | type_value)>
|
||||||
|
{
|
||||||
|
const hits : Array<
|
||||||
|
{
|
||||||
|
id : _espe.type.admin_id;
|
||||||
|
preview : _espe.type.admin_object;
|
||||||
|
}
|
||||||
|
> = await _espe.repository.admin.list(name);
|
||||||
|
return Promise.resolve<(null | type_value)>(
|
||||||
|
(hits.length === 1)
|
||||||
|
?
|
||||||
|
{"id": hits[0].id, "object": hits[0].preview}
|
||||||
|
:
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async function check_password(
|
||||||
|
value : type_value,
|
||||||
|
password_given : string
|
||||||
|
) : Promise<boolean>
|
||||||
|
{
|
||||||
|
return _espe.helpers.bcrypt_compare(value.object.password_image, password_given);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function login(
|
||||||
|
name : string,
|
||||||
|
password : string
|
||||||
|
) : Promise<(null | type_value)>
|
||||||
|
{
|
||||||
|
const value : (null | type_value) = await get(name);
|
||||||
|
if (value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* @todo outsource fail count threshold?
|
||||||
|
*/
|
||||||
|
const password_fail_threshold : int = 3;
|
||||||
|
if (value.object.password_fail_count >= password_fail_threshold) {
|
||||||
|
lib_plankton.log.notice(
|
||||||
|
"admin_password_fail_threshold_exceeded",
|
||||||
|
{
|
||||||
|
"id": value.id,
|
||||||
|
"name": value.object.name,
|
||||||
|
"password_fail_count": (value.object.password_fail_count + 1)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await _espe.repository.admin.update(
|
||||||
|
value.id,
|
||||||
|
{
|
||||||
|
"name": value.object.name,
|
||||||
|
"email_address": value.object.email_address,
|
||||||
|
"password_image": value.object.password_image,
|
||||||
|
"password_fail_count": (value.object.password_fail_count + 1),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (! (await check_password(value, password))) {
|
||||||
|
await _espe.repository.admin.update(
|
||||||
|
value.id,
|
||||||
|
{
|
||||||
|
"name": value.object.name,
|
||||||
|
"email_address": value.object.email_address,
|
||||||
|
"password_image": value.object.password_image,
|
||||||
|
"password_fail_count": (value.object.password_fail_count + 1),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function email_addresses(
|
||||||
|
) : Promise<Array<string>>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
(await _espe.repository.admin.list(null))
|
||||||
|
.map(hit => hit.preview["email_address"])
|
||||||
|
.filter(x => (x !== null))
|
||||||
|
) as Array<string>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function notify_all(
|
||||||
|
subject : string,
|
||||||
|
body : string
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
return _espe.helpers.email_send(
|
||||||
|
await email_addresses(),
|
||||||
|
subject,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -503,7 +503,7 @@ namespace _espe.service.member
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
/*await*/ _espe.helpers.notify_admins(
|
/*await*/ _espe.service.admin.notify_all(
|
||||||
lib_plankton.string.coin(
|
lib_plankton.string.coin(
|
||||||
"{{head}} | {{core}}",
|
"{{head}} | {{core}}",
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,21 @@ You should have received a copy of the GNU General Public License along with thi
|
||||||
namespace _espe.type
|
namespace _espe.type
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type admin_id = int;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type admin_object = {
|
||||||
|
name : string;
|
||||||
|
email_address : (null | string);
|
||||||
|
password_image : string;
|
||||||
|
password_fail_count : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export type member_id = int;
|
export type member_id = int;
|
||||||
|
|
|
@ -48,11 +48,12 @@ ${dir_temp}/espe-core.js ${dir_temp}/espe-core.d.ts: \
|
||||||
${dir_source}/helpers/password.ts \
|
${dir_source}/helpers/password.ts \
|
||||||
${dir_source}/database.ts \
|
${dir_source}/database.ts \
|
||||||
${dir_source}/types.ts \
|
${dir_source}/types.ts \
|
||||||
|
${dir_source}/repositories/admin.ts \
|
||||||
${dir_source}/repositories/name_index.ts \
|
${dir_source}/repositories/name_index.ts \
|
||||||
${dir_source}/repositories/member.ts \
|
${dir_source}/repositories/member.ts \
|
||||||
${dir_source}/services/name_index.ts \
|
${dir_source}/services/name_index.ts \
|
||||||
${dir_source}/services/member.ts \
|
${dir_source}/services/member.ts \
|
||||||
${dir_source}/service-admin.ts \
|
${dir_source}/services/admin.ts \
|
||||||
${dir_source}/api/base.ts \
|
${dir_source}/api/base.ts \
|
||||||
${dir_source}/api/actions/meta_ping.ts \
|
${dir_source}/api/actions/meta_ping.ts \
|
||||||
${dir_source}/api/actions/meta_spec.ts \
|
${dir_source}/api/actions/meta_spec.ts \
|
||||||
|
|
Loading…
Add table
Reference in a new issue