[mod] rename:authelia-calls->arc
This commit is contained in:
parent
045330759b
commit
468ac9a23e
5 changed files with 267 additions and 247 deletions
29
notes.md
29
notes.md
|
@ -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<string>;
|
|
||||||
password : string;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
type type_payload = {
|
|
||||||
timestamp : int;
|
|
||||||
authhash : string;
|
|
||||||
data : type_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
34
readme.md
Normal file
34
readme.md
Normal file
|
@ -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
|
||||||
|
|
439
source/main.ts
439
source/main.ts
|
@ -170,239 +170,254 @@ async function main(
|
||||||
const conf : type_conf = await conf_get(args.conf_path);
|
const conf : type_conf = await conf_get(args.conf_path);
|
||||||
|
|
||||||
// exec
|
// 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",
|
lib_plankton.rest.register<
|
||||||
"versioning_method": "header",
|
null,
|
||||||
"versioning_header_name": "X-Api-Version",
|
string
|
||||||
"set_access_control_headers": true,
|
>
|
||||||
"authentication": {
|
(
|
||||||
"kind": "key_header",
|
rest_subject,
|
||||||
"parameters": {"name": "X-Session-Key"}
|
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": () => ({
|
||||||
lib_plankton.rest.register<
|
"nullable": true,
|
||||||
null,
|
}),
|
||||||
string
|
"output_schema": () => ({
|
||||||
>
|
"nullable": false,
|
||||||
(
|
"type": "string",
|
||||||
rest_subject,
|
}),
|
||||||
lib_plankton.http.enum_method.get,
|
// "restriction": restriction_none,
|
||||||
"/meta/ping",
|
"execution": () => {
|
||||||
{
|
return Promise.resolve({
|
||||||
"description": "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen",
|
"status_code": 200,
|
||||||
"input_schema": () => ({
|
"data": "pong",
|
||||||
"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,
|
lib_plankton.rest.register<
|
||||||
"description": "authorization string",
|
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,
|
lib_plankton.rest.register<
|
||||||
"type": "object",
|
type_user_list_sparse,
|
||||||
"additionalProperties": false,
|
null
|
||||||
"properties": {
|
>(
|
||||||
"users": {
|
rest_subject,
|
||||||
"nullable": true,
|
lib_plankton.http.enum_method.put,
|
||||||
"type": "object",
|
"/users/set",
|
||||||
"additionalProperties": {
|
{
|
||||||
"nullable": false,
|
"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",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": {
|
||||||
"properties": {
|
"nullable": false,
|
||||||
"disabled": {
|
"type": "object",
|
||||||
"nullable": false,
|
"additionalProperties": false,
|
||||||
"type": "boolean",
|
"properties": {
|
||||||
},
|
"disabled": {
|
||||||
"displayname": {
|
|
||||||
"nullable": false,
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"nullable": false,
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"groups": {
|
|
||||||
"nullable": false,
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"type": "string"
|
"type": "boolean",
|
||||||
}
|
},
|
||||||
},
|
"displayname": {
|
||||||
"password": {
|
"nullable": false,
|
||||||
"nullable": false,
|
"type": "string",
|
||||||
"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": [
|
"properties": {},
|
||||||
"password",
|
"required": [],
|
||||||
]
|
|
||||||
},
|
|
||||||
"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,
|
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
return false;
|
"required": [
|
||||||
}
|
"users"
|
||||||
else {
|
]
|
||||||
const authhash_is : string = stuff.query_parameters["auth"];
|
}),
|
||||||
const authhash_shall : string = lib_plankton.sha256.get(
|
"output_schema": () => ({
|
||||||
timestamp_remote.toFixed(0) + conf.authentication.hash_salt
|
"nullable": true,
|
||||||
);
|
}),
|
||||||
if (authhash_is !== authhash_shall) {
|
"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(
|
lib_plankton.log.notice(
|
||||||
"restriction_access_denied_due_to_mismatching_hashes",
|
"restriction_access_denied_due_to_invalid_timestamp",
|
||||||
{
|
{
|
||||||
"timestamp": timestamp_remote,
|
"timestamp_local": timestamp_local,
|
||||||
"authhash_is": authhash_is,
|
"timestamp_remote": timestamp_remote,
|
||||||
"authhash_shall": authhash_shall,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
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) => {
|
||||||
"execution": async (stuff) => {
|
await lib_plankton.file.write(
|
||||||
await lib_plankton.file.write(
|
conf.authelia.usersfile_path,
|
||||||
conf.authelia.usersfile_path,
|
encode_user_list(stuff.input)
|
||||||
encode_user_list(stuff.input)
|
);
|
||||||
);
|
lib_plankton.log.notice(
|
||||||
lib_plankton.log.notice(
|
"userdata_updated",
|
||||||
"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<lib_plankton.http.type_response>(
|
||||||
|
{
|
||||||
|
"version": "HTTP/1.1",
|
||||||
|
"status_code": 500,
|
||||||
|
"headers": {},
|
||||||
|
// @ts-ignore
|
||||||
|
"body": Buffer.from(""),
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
return Promise.resolve({
|
.then<string>(
|
||||||
"status_code": 200,
|
(http_response) => {
|
||||||
"data": null
|
const output : string = lib_plankton.http.encode_response(http_response);
|
||||||
});
|
return Promise.resolve<string>(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<lib_plankton.http.type_response>(
|
|
||||||
{
|
|
||||||
"version": "HTTP/1.1",
|
|
||||||
"status_code": 500,
|
|
||||||
"headers": {},
|
|
||||||
// @ts-ignore
|
|
||||||
"body": Buffer.from(""),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then<string>(
|
|
||||||
(http_response) => {
|
|
||||||
const output : string = lib_plankton.http.encode_response(http_response);
|
|
||||||
return Promise.resolve<string>(output);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"host": conf.server.host,
|
|
||||||
"port": conf.server.port,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
lib_plankton.server.start(server);
|
|
||||||
|
|
||||||
return Promise.resolve<void>(undefined);
|
return Promise.resolve<void>(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ def main():
|
||||||
"-o",
|
"-o",
|
||||||
"--output-directory",
|
"--output-directory",
|
||||||
type = str,
|
type = str,
|
||||||
default = "/tmp/authelia-calls",
|
default = "/tmp/arc",
|
||||||
metavar = "<output-directory>",
|
metavar = "<output-directory>",
|
||||||
help = "output directory",
|
help = "output directory",
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
dir_tools := tools
|
dir_tools := tools
|
||||||
dir_lib := lib
|
dir_lib := lib
|
||||||
dir_source := source
|
dir_source := source
|
||||||
dir_temp := /tmp/authelia-calls-temp
|
dir_temp := /tmp/arc-temp
|
||||||
dir_build := build
|
dir_build := build
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,18 +19,18 @@ cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc
|
||||||
## rules
|
## rules
|
||||||
|
|
||||||
.PHONY: _default
|
.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_lib}/plankton/plankton.d.ts \
|
||||||
${dir_source}/main.ts
|
${dir_source}/main.ts
|
||||||
@ ${cmd_log} "compiling …"
|
@ ${cmd_log} "compiling …"
|
||||||
@ ${cmd_tsc} --lib es2020 --strict $^ --outFile $@
|
@ ${cmd_tsc} --lib es2020 --strict $^ --outFile $@
|
||||||
|
|
||||||
${dir_build}/authelia-calls: \
|
${dir_build}/arc: \
|
||||||
${dir_source}/head.js \
|
${dir_source}/head.js \
|
||||||
${dir_lib}/plankton/plankton.js \
|
${dir_lib}/plankton/plankton.js \
|
||||||
${dir_temp}/authelia-calls-unlinked.js
|
${dir_temp}/arc-unlinked.js
|
||||||
@ ${cmd_log} "linking …"
|
@ ${cmd_log} "linking …"
|
||||||
@ ${cmd_mkdir} $(dir $@)
|
@ ${cmd_mkdir} $(dir $@)
|
||||||
@ ${cmd_cat} $^ > $@
|
@ ${cmd_cat} $^ > $@
|
||||||
|
|
Loading…
Add table
Reference in a new issue