core/source/main.ts

333 lines
7.1 KiB
TypeScript
Raw Normal View History

2024-08-18 13:57:55 +02:00
/**
*/
type type_conf = {
server : {
port : int;
};
authentication : {
timestamp_tolerance : float;
hash_salt : string;
};
authelia : {
usersfile_path : string;
};
};
/**
*/
type type_user_list_sparse = (
null
|
Record<
string,
{
disabled ?: boolean;
displayname ?: string;
email ?: string;
groups ?: Array<string>;
password : string;
}
>
);
/**
*/
type type_user_list_complete = Record<
string,
{
disabled : boolean;
displayname : string;
email : string;
groups : Array<string>;
password : string;
}
>;
/**
*/
async function conf_get(
path : string
) : Promise<type_conf>
{
const raw : any = lib_plankton.json.decode(await lib_plankton.file.read(path));;
return {
"server": {
"port": ((raw["server"] ?? {})["port"] ?? 4466),
},
"authentication": {
"timestamp_tolerance": (raw["authentication"]["timestamp_tolerance"] ?? 2.0),
"hash_salt": raw["authentication"]["hash_salt"], // required
},
"authelia": {
"usersfile_path": ((raw["authelia"] ?? {})["usersfile_path"] ?? "/var/authelia/users.yaml"),
},
};
}
/**
*/
function encode_user_list(
user_list : type_user_list_sparse
) : string
{
let output : string = "";
Object.entries(user_list ?? {}).forEach(
([key, value]) => {
output += (key + ":\n");
output += (" " + "disabled: " + ((value.disabled ?? false) ? "true" : "false") + "\n");
output += (" " + "displayname: " + (value.displayname ?? key) + "\n");
if ("email" in value) {
output += (" " + "email: " + value.email + "\n");
}
else {
// do nothing
}
output += (" " + "groups: " + JSON.stringify(value.groups ?? []) + "\n");
output += (" " + "password: " + value.password + "\n");
}
);
return output;
}
/**
*/
async function main(
args_raw : Array<string>
) : Promise<void>
{
lib_plankton.log.conf_push(
[
lib_plankton.log.channel_make(
{
"kind": "stdout",
"data": {
"threshold": "info",
// "format": "human_readable",
}
}
),
]
);
// args
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({
"conf_path": lib_plankton.args.class_argument.volatile({
"indicators_long": ["conf_path"],
"indicators_short": ["c"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "conf.json",
"info": "path to conf file",
"name": "conf-path",
}),
"help": lib_plankton.args.class_argument.volatile({
"indicators_long": ["help"],
"indicators_short": ["h"],
"type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace,
"default": false,
"info": "only show help and exit",
"name": "help",
}),
});
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
const conf : type_conf = await conf_get(args.conf_path);
// exec
const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make(
{
"title": "authelia-callbacks",
"versioning_method": "header",
"versioning_header_name": "X-Api-Version",
"set_access_control_headers": true,
"authentication": {
"kind": "key_header",
"parameters": {"name": "X-Session-Key"}
},
}
);
{
lib_plankton.rest.register<
null,
string
>
(
rest_subject,
lib_plankton.http.enum_method.get,
"/meta/ping",
{
"description": "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen",
"input_schema": () => ({
"nullable": true,
}),
"output_schema": () => ({
"nullable": false,
"type": "string",
}),
// "restriction": restriction_none,
"execution": () => {
return Promise.resolve({
"status_code": 200,
"data": "pong",
});
},
}
);
lib_plankton.rest.register<
null,
any
>
(
rest_subject,
lib_plankton.http.enum_method.get,
"/meta/spec",
{
"description": "gibt die API-Spezifikation im OpenAPI-Format aus",
"input_schema": () => ({
"nullable": true,
}),
"output_schema": () => ({
}),
// "restriction": restriction_none,
"execution": () => {
return Promise.resolve({
"status_code": 200,
"data": lib_plankton.rest.to_oas(rest_subject),
});
},
}
);
lib_plankton.rest.register<
type_user_list_sparse,
null
>(
rest_subject,
lib_plankton.http.enum_method.put,
"/users/set",
{
"description": "setzt die Nutzerliste",
"query_parameters": [
{
"name": "timestamp",
"required": true,
"description": "current UNIX timestamp",
},
{
"name": "auth",
"required": true,
"description": "authorization string",
},
],
"input_schema": () => ({
"nullable": true,
"type": "object",
"additionalProperties": {
"nullable": false,
"type": "object",
"additionalProperties": false,
"properties": {
"disabled": {
"nullable": false,
"type": "boolean",
},
"displayname": {
"nullable": false,
"type": "string",
},
"email": {
"nullable": false,
"type": "string",
},
"groups": {
"nullable": false,
"type": "array",
"items": {
"nullable": false,
"type": "string"
}
},
"password": {
"nullable": false,
"type": "string",
},
},
"required": [
"password",
]
},
"properties": {},
"required": [],
}),
"output_schema": () => ({
"nullable": true,
}),
"restriction": async (stuff) => {
const timestamp_local : float = lib_plankton.base.get_current_timestamp();
const timestamp_remote : float = parseFloat(stuff.query_parameters["timestamp"]);
if (Math.abs(timestamp_local - timestamp_remote) > conf.authentication.timestamp_tolerance) {
return false;
}
else {
const authhash_is : string = stuff.query_parameters["auth"];
const authhash_shall : string = lib_plankton.sha256.get(
timestamp_remote.toFixed(0) + conf.authentication.hash_salt
);
if (authhash_is !== authhash_shall) {
return false;
}
else {
return true;
}
}
},
"execution": async (stuff) => {
await lib_plankton.file.write(
conf.authelia.usersfile_path,
encode_user_list(stuff.input)
);
return Promise.resolve({
"status_code": 200,
"data": null
});
},
}
);
}
const server : lib_plankton.server.type_subject = lib_plankton.server.make(
conf.server.port,
async (input, metadata) => {
const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input);
const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call(
rest_subject,
http_request
);
const output : string = lib_plankton.http.encode_response(http_response);
return output;
}
);
lib_plankton.server.start(server);
return Promise.resolve<void>(undefined);
}
(
main(process.argv.slice(2))
.then(
() => {}
)
.catch(
(error) => {process.stderr.write(String(error));}
)
);