[mod] notifications

This commit is contained in:
roydfalk 2024-05-27 21:27:36 +02:00
parent 1483bf95a8
commit 8a33c029c7
4 changed files with 252 additions and 119 deletions

View file

@ -29,6 +29,7 @@ namespace _espe.api
email_use_nominal_address : boolean; email_use_nominal_address : boolean;
email_redirect_to_private_address : boolean; email_redirect_to_private_address : boolean;
password : (null | string); password : (null | string);
notification_target_url_template ?: (null | string);
}, },
Array< Array<
{ {
@ -67,6 +68,11 @@ namespace _espe.api
"nullable": true, "nullable": true,
"description": "Passwort für alle Netz-Dienste", "description": "Passwort für alle Netz-Dienste",
}, },
"notification_target_url_template": {
"type": "string",
"nullable": true,
"description": "Platz-Halter: id"
},
}, },
"required": [ "required": [
"email_use_veiled_address", "email_use_veiled_address",
@ -128,6 +134,9 @@ namespace _espe.api
"email_use_nominal_address": input.email_use_nominal_address, "email_use_nominal_address": input.email_use_nominal_address,
"email_redirect_to_private_address": input.email_redirect_to_private_address, "email_redirect_to_private_address": input.email_redirect_to_private_address,
"password": input.password, "password": input.password,
},
{
"notification_target_url_template": (input.notification_target_url_template ?? null),
} }
) )
.then( .then(

View file

@ -96,12 +96,21 @@ namespace _espe.conf
settings : { settings : {
target_domain : string; target_domain : string;
frontend_url_base : (null | string); frontend_url_base : (null | string);
login_url : (null | string);
prefix_for_nominal_email_addresses : string; prefix_for_nominal_email_addresses : string;
facultative_membership_number : boolean; facultative_membership_number : boolean;
summon_email : {
subject : string;
body : string;
};
registration_email : { registration_email : {
subject : string; subject : string;
body : string; body : string;
}; };
activation_email : {
subject : string;
body : string;
};
password_policy : { password_policy : {
minimum_length : (null | int); minimum_length : (null | int);
maximum_length : (null | int); maximum_length : (null | int);
@ -248,12 +257,21 @@ namespace _espe.conf
((node_settings) => ({ ((node_settings) => ({
"target_domain": (node_settings["target_domain"] ?? "example.org"), "target_domain": (node_settings["target_domain"] ?? "example.org"),
"frontend_url_base": (node_settings["frontend_url_base"] ?? null), // TODO: mandatory? "frontend_url_base": (node_settings["frontend_url_base"] ?? null), // TODO: mandatory?
"login_url": (node_settings["login_url"] ?? null),
"prefix_for_nominal_email_addresses": (node_settings["prefix_for_nominal_email_addresses"] ?? "member-"), "prefix_for_nominal_email_addresses": (node_settings["prefix_for_nominal_email_addresses"] ?? "member-"),
"facultative_membership_number": (node_settings["facultative_membership_number"] ?? false), "facultative_membership_number": (node_settings["facultative_membership_number"] ?? false),
"summon_email": {
"subject": ((node_settings["summon_email"] ?? {})["subject"] ?? "Please register"),
"body": ((node_settings["summon_email"] ?? {})["body"] ?? "URL: {{url}}"),
},
"registration_email": { "registration_email": {
"subject": ((node_settings["registration_email"] ?? {})["subject"] ?? "Registration"), "subject": ((node_settings["registration_email"] ?? {})["subject"] ?? "Mmeber registered"),
"body": ((node_settings["registration_email"] ?? {})["body"] ?? "URL: {{url}}"), "body": ((node_settings["registration_email"] ?? {})["body"] ?? "URL: {{url}}"),
}, },
"activation_email": {
"subject": ((node_settings["activation_email"] ?? {})["subject"] ?? "Account activated"),
"body": ((node_settings["activation_email"] ?? {})["body"] ?? "URL: {{url}}\n\nLogin name: {{name_login}}\n\n"),
},
"password_policy": ( "password_policy": (
((node_settings_password_policy) => ({ ((node_settings_password_policy) => ({
"minimum_length": ( "minimum_length": (

View file

@ -268,4 +268,64 @@ namespace _espe.helpers
} }
} }
/**
*/
export function frontend_url_check(
) : void
{
const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base;
if (frontend_url_base === null) {
throw (new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!"));
}
else {
// do nothing
}
}
/**
*/
export function frontend_url_get(
template : string,
arguments_ : Record<string, string>
) : (null | string)
{
const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base;
if (frontend_url_base === null) {
// throw (new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!"));
return null;
}
else {
return lib_plankton.string.coin(
"{{base}}/{{rest}}",
{
"base": frontend_url_base,
"rest": lib_plankton.string.coin(template, arguments_),
}
);
}
}
/**
*/
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
);
}
} }

View file

@ -33,7 +33,7 @@ namespace _espe.service.member
/** /**
*/ */
function notify_change( function signal_change(
) : void ) : void
{ {
_hooks_change.forEach( _hooks_change.forEach(
@ -251,6 +251,40 @@ namespace _espe.service.member
); );
} }
/**
*/
async function send_activation_email(
member_object : _espe.type.member_object
) : Promise<void>
{
if (! member_object.enabled) {
// do nothing
}
else {
if (member_object.email_address_private === null) {
// do nothing
}
else {
await _espe.helpers.email_send(
[
member_object.email_address_private,
],
_espe.conf.get().settings.activation_email.subject,
lib_plankton.string.coin(
_espe.conf.get().settings.activation_email.body,
{
"name_display": name_display(member_object),
"name_login": name_login(member_object),
"url": (_espe.conf.get().settings.login_url ?? "--"),
}
)
);
}
}
}
/** /**
* gibt die vollständigen Daten aller Mitglieder aus * gibt die vollständigen Daten aller Mitglieder aus
*/ */
@ -330,47 +364,49 @@ namespace _espe.service.member
"password_change_token": null, "password_change_token": null,
}; };
const id : _espe.type.member_id = await _espe.repository.member.create(object); const id : _espe.type.member_id = await _espe.repository.member.create(object);
notify_change(); signal_change();
return id; return id;
} }
/** /**
* sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung * sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung
*
* @todo conf:settings.frontend_url_base verwenden
*/ */
export async function summon( export async function summon(
member_id : _espe.type.member_id, member_id : _espe.type.member_id,
url_template : string url_template : string
) : Promise<(null | string)> ) : Promise<(null | string)>
{ {
_espe.helpers.frontend_url_check();
const member_object : _espe.type.member_object = await get(member_id); const member_object : _espe.type.member_object = await get(member_id);
if (member_object.email_address_private === null) { if (member_object.email_address_private === null) {
return null; return null;
} }
else { else {
const verification : string = await _espe.helpers.verification_get(member_id); const url : (null | string) = _espe.helpers.frontend_url_get(
const url : string = lib_plankton.string.coin(
url_template, url_template,
{ {
"verification": verification, "verification": await _espe.helpers.verification_get(member_id),
} }
); );
if (url === null) {
// do nothing
}
else {
await _espe.helpers.email_send( await _espe.helpers.email_send(
[ [
member_object.email_address_private, member_object.email_address_private,
], ],
_espe.conf.get().settings.registration_email.subject, _espe.conf.get().settings.summon_email.subject,
lib_plankton.string.coin( lib_plankton.string.coin(
_espe.conf.get().settings.registration_email.body, _espe.conf.get().settings.summon_email.body,
{ {
"name": member_object.name_real_value, "name": name_display(member_object),
"url": url, "url": url,
} }
) )
); );
}
return url; return url;
} }
} }
@ -425,13 +461,13 @@ namespace _espe.service.member
password : (null | string); password : (null | string);
}, },
options : { options : {
notify_admins ?: boolean; notification_target_url_template ?: (null | string);
} = {} } = {}
) : Promise<Array<{incident : string; details : Record<string, any>;}>> ) : Promise<Array<{incident : string; details : Record<string, any>;}>>
{ {
options = Object.assign( options = Object.assign(
{ {
"notify_admins": false, "notification_target_url_template": null,
}, },
options options
); );
@ -472,26 +508,40 @@ namespace _espe.service.member
member_object.password_image = await password_image(data.password); member_object.password_image = await password_image(data.password);
member_object.registered = true; member_object.registered = true;
await _espe.repository.member.update(member_id, member_object); await _espe.repository.member.update(member_id, member_object);
signal_change();
notify_change(); {
const url : (null | string) = (
if (! options.notify_admins) { (
(options.notification_target_url_template === undefined)
||
(options.notification_target_url_template === null)
)
? null
: _espe.helpers.frontend_url_get(
options.notification_target_url_template,
{
"id": member_id.toFixed(0),
}
)
);
if (url === null) {
// do nothing // do nothing
} }
else { else {
await _espe.helpers.email_send( /*await*/ _espe.helpers.notify_admins(
( _espe.conf.get().settings.registration_email.subject,
( lib_plankton.string.coin(
_espe.conf.get().admins _espe.conf.get().settings.registration_email.body,
.map(admin => admin.email_address) {
.filter(x => (x !== null)) "name_display": name_display(member_object),
) as Array<string> "url": url,
), }
"Eingegangene Registrierung", // TODO )
("Member-ID: " + member_id.toFixed(0))
); );
} }
} }
/*await*/ send_activation_email(member_object);
}
return Promise.resolve(flaws); return Promise.resolve(flaws);
} }
@ -526,7 +576,8 @@ namespace _espe.service.member
"password_change_token": member_object_old.password_change_token, "password_change_token": member_object_old.password_change_token,
}; };
await _espe.repository.member.update(member_id, member_object_new); await _espe.repository.member.update(member_id, member_object_new);
notify_change(); signal_change();
/*await*/ send_activation_email(member_object_new);
} }
@ -540,11 +591,7 @@ namespace _espe.service.member
url_template : string url_template : string
) : Promise<void> ) : Promise<void>
{ {
const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base; _espe.helpers.frontend_url_check();
if (frontend_url_base === null) {
return Promise.reject<void>(new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!"));
}
else {
const now : int = lib_plankton.base.get_current_timestamp(true); const now : int = lib_plankton.base.get_current_timestamp(true);
const cooldown_time : int = _espe.conf.get().settings.password_change.cooldown_time; const cooldown_time : int = _espe.conf.get().settings.password_change.cooldown_time;
const member_ids : Array<_espe.type.member_id> = await ( const member_ids : Array<_espe.type.member_id> = await (
@ -616,8 +663,19 @@ namespace _espe.service.member
"password_change_token": token, "password_change_token": token,
}; };
await _espe.repository.member.update(member_id, member_object_new); await _espe.repository.member.update(member_id, member_object_new);
// notify_change(); // signal_change();
// do NOT wait in order to reduce information for potential attackers // do NOT wait in order to reduce information for potential attackers
const url : (null | string) = _espe.helpers.frontend_url_get(
url_template,
{
"id": member_id.toFixed(0),
"token": token,
}
);
if (url === null) {
// do nothing
}
else {
/*await*/ _espe.helpers.email_send( /*await*/ _espe.helpers.email_send(
[ [
member_object_old.email_address_private, member_object_old.email_address_private,
@ -627,19 +685,7 @@ namespace _espe.service.member
_espe.conf.get().settings.password_change.initialization_email.body, _espe.conf.get().settings.password_change.initialization_email.body,
{ {
"name": name_display(member_object_old), "name": name_display(member_object_old),
"url": lib_plankton.string.coin( "url": url,
"{{base}}{{rest}}",
{
"base": frontend_url_base,
"rest": lib_plankton.string.coin(
url_template,
{
"id": member_id.toFixed(0),
"token": token,
}
),
}
),
} }
) )
); );
@ -706,7 +752,7 @@ namespace _espe.service.member
"password_change_token": null, "password_change_token": null,
}; };
await _espe.repository.member.update(member_id, member_object_new); await _espe.repository.member.update(member_id, member_object_new);
notify_change(); signal_change();
await _espe.helpers.email_send( await _espe.helpers.email_send(
[ [
member_object_old.email_address_private, member_object_old.email_address_private,
@ -732,7 +778,7 @@ namespace _espe.service.member
) : Promise<void> ) : Promise<void>
{ {
await _espe.repository.member.delete(id); await _espe.repository.member.delete(id);
notify_change(); signal_change();
} }
*/ */