diff --git a/notes.md b/notes.md deleted file mode 100644 index a54bea6..0000000 --- a/notes.md +++ /dev/null @@ -1,29 +0,0 @@ -- siehe `~/projekte/linke/sonstiges/authelia-callbacks` - -- Espe macht HTTP-Request zu `callbacks.authelia.linke.sx` mit Nutzerliste als Rumpf (Authentifizierung?) - -- submit - - timestamp - - userlist - - authhash (`sha256(timestamp+secret)`) - - -``` -type type_data = Record< - string, - { - disabled : boolean; - displayname : string; - email : string; - groups : Array; - password : string; - } ->; - -type type_payload = { - timestamp : int; - authhash : string; - data : type_data; -} - -``` diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a739727 --- /dev/null +++ b/readme.md @@ -0,0 +1,34 @@ +# ARC + +## Beschreibung + +- ARC = **A**uthelia **R**emote **C**ontrol +- Dienst zur Steuerung einer [Authelia](https://www.authelia.com/)-Instanz + + +## Erstellung + +### Voraussetzungen + +- GNU Make +- NodeJS + + +### Anweisungen + +- `tools/build` ausführen + + +## Betrieb + +### Voraussetzungen + +- NodeJS + + +### Anweisungen + +- ins Erzeugnis-Verzeichnis wechseln +- `./arc -h` ausführen +- für die meisten Anwendungsfälle ist es erforderlich eine Konfigurations-Datei anzulegen + diff --git a/source/main.ts b/source/main.ts index ee0515b..6bcec87 100644 --- a/source/main.ts +++ b/source/main.ts @@ -170,239 +170,254 @@ async function main( const conf : type_conf = await conf_get(args.conf_path); // exec - const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make( + if (args["help"]) { + process.stdout.write( + arg_handler.generate_help( + { + "programname": "arc", + "description": "Authelia Remote Control", + "executable": "arc", + } + ) + + + "\n" + ); + } + else { + 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"} + }, + } + ); { - "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", + 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", + }); }, - { - "name": "auth", - "required": true, - "description": "authorization string", + } + ); + 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), + }); }, - ], - "input_schema": () => ({ - "nullable": true, - "type": "object", - "additionalProperties": false, - "properties": { - "users": { - "nullable": true, - "type": "object", - "additionalProperties": { - "nullable": false, + } + ); + 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": false, + "properties": { + "users": { + "nullable": true, "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": { + "additionalProperties": { + "nullable": false, + "type": "object", + "additionalProperties": false, + "properties": { + "disabled": { "nullable": false, - "type": "string" - } - }, - "password": { - "nullable": false, - "type": "string", + "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", + ] }, - "required": [ - "password", - ] - }, - "properties": {}, - "required": [], - } - }, - "required": [ - "users" - ] - }), - "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) { - lib_plankton.log.notice( - "restriction_access_denied_due_to_invalid_timestamp", - { - "timestamp_local": timestamp_local, - "timestamp_remote": timestamp_remote, + "properties": {}, + "required": [], } - ); - 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) { + }, + "required": [ + "users" + ] + }), + "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) { lib_plankton.log.notice( - "restriction_access_denied_due_to_mismatching_hashes", + "restriction_access_denied_due_to_invalid_timestamp", { - "timestamp": timestamp_remote, - "authhash_is": authhash_is, - "authhash_shall": authhash_shall, + "timestamp_local": timestamp_local, + "timestamp_remote": timestamp_remote, } ); return false; } else { - return true; + 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) { + lib_plankton.log.notice( + "restriction_access_denied_due_to_mismatching_hashes", + { + "timestamp": timestamp_remote, + "authhash_is": authhash_is, + "authhash_shall": authhash_shall, + } + ); + return false; + } + else { + return true; + } } - } - }, - "execution": async (stuff) => { - await lib_plankton.file.write( - conf.authelia.usersfile_path, - encode_user_list(stuff.input) - ); - lib_plankton.log.notice( - "userdata_updated", - { + }, + "execution": async (stuff) => { + await lib_plankton.file.write( + conf.authelia.usersfile_path, + encode_user_list(stuff.input) + ); + lib_plankton.log.notice( + "userdata_updated", + { + } + ); + return Promise.resolve({ + "status_code": 200, + "data": null + }); + }, + } + ); + } + + const server : lib_plankton.server.type_subject = lib_plankton.server.make( + async (input, metadata) => { + const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString()); + return ( + lib_plankton.rest.call( + rest_subject, + http_request + ) + .catch( + (error) => { + lib_plankton.log.error( + "server_request_processing_failed", + { + "error": String(error), + } + ); + return Promise.resolve( + { + "version": "HTTP/1.1", + "status_code": 500, + "headers": {}, + // @ts-ignore + "body": Buffer.from(""), + } + ); } - ); - return Promise.resolve({ - "status_code": 200, - "data": null - }); - }, + ) + .then( + (http_response) => { + const output : string = lib_plankton.http.encode_response(http_response); + return Promise.resolve(output); + } + ) + ); + }, + { + "host": conf.server.host, + "port": conf.server.port, } ); + + lib_plankton.server.start(server); } - const server : lib_plankton.server.type_subject = lib_plankton.server.make( - async (input, metadata) => { - const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString()); - return ( - lib_plankton.rest.call( - rest_subject, - http_request - ) - .catch( - (error) => { - lib_plankton.log.error( - "server_request_processing_failed", - { - "error": String(error), - } - ); - return Promise.resolve( - { - "version": "HTTP/1.1", - "status_code": 500, - "headers": {}, - // @ts-ignore - "body": Buffer.from(""), - } - ); - } - ) - .then( - (http_response) => { - const output : string = lib_plankton.http.encode_response(http_response); - return Promise.resolve(output); - } - ) - ); - }, - { - "host": conf.server.host, - "port": conf.server.port, - } - ); - - lib_plankton.server.start(server); - return Promise.resolve(undefined); } diff --git a/tools/build b/tools/build index ff1020f..7dd93df 100755 --- a/tools/build +++ b/tools/build @@ -12,7 +12,7 @@ def main(): "-o", "--output-directory", type = str, - default = "/tmp/authelia-calls", + default = "/tmp/arc", metavar = "", help = "output directory", ) diff --git a/tools/makefile b/tools/makefile index 707e2d4..674a463 100644 --- a/tools/makefile +++ b/tools/makefile @@ -3,7 +3,7 @@ dir_tools := tools dir_lib := lib dir_source := source -dir_temp := /tmp/authelia-calls-temp +dir_temp := /tmp/arc-temp dir_build := build @@ -19,18 +19,18 @@ cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc ## rules .PHONY: _default -_default: ${dir_build}/authelia-calls +_default: ${dir_build}/arc -${dir_temp}/authelia-calls-unlinked.js: \ +${dir_temp}/arc-unlinked.js: \ ${dir_lib}/plankton/plankton.d.ts \ ${dir_source}/main.ts @ ${cmd_log} "compiling …" @ ${cmd_tsc} --lib es2020 --strict $^ --outFile $@ -${dir_build}/authelia-calls: \ +${dir_build}/arc: \ ${dir_source}/head.js \ ${dir_lib}/plankton/plankton.js \ - ${dir_temp}/authelia-calls-unlinked.js + ${dir_temp}/arc-unlinked.js @ ${cmd_log} "linking …" @ ${cmd_mkdir} $(dir $@) @ ${cmd_cat} $^ > $@