2024-08-18 13:57:55 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
type type_conf = {
|
|
|
|
server : {
|
2024-08-21 15:07:27 +02:00
|
|
|
host : string;
|
2024-08-18 13:57:55 +02:00
|
|
|
port : int;
|
|
|
|
};
|
|
|
|
authentication : {
|
|
|
|
timestamp_tolerance : float;
|
|
|
|
hash_salt : string;
|
|
|
|
};
|
|
|
|
authelia : {
|
|
|
|
usersfile_path : string;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
type type_user_list_sparse = (
|
|
|
|
null
|
|
|
|
|
|
2024-08-21 15:07:27 +02:00
|
|
|
{
|
|
|
|
users : Record<
|
|
|
|
string,
|
|
|
|
{
|
|
|
|
disabled ?: boolean;
|
|
|
|
displayname ?: string;
|
|
|
|
email ?: string;
|
|
|
|
groups ?: Array<string>;
|
|
|
|
password : string;
|
|
|
|
}
|
|
|
|
>;
|
|
|
|
}
|
2024-08-18 13:57:55 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
2024-08-21 15:07:27 +02:00
|
|
|
type type_user_list_complete = {
|
|
|
|
users : Record<
|
|
|
|
string,
|
|
|
|
{
|
|
|
|
disabled : boolean;
|
|
|
|
displayname : string;
|
|
|
|
email : string;
|
|
|
|
groups : Array<string>;
|
|
|
|
password : string;
|
|
|
|
}
|
|
|
|
>;
|
|
|
|
};
|
2024-08-18 13:57:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
async function conf_get(
|
|
|
|
path : string
|
|
|
|
) : Promise<type_conf>
|
|
|
|
{
|
|
|
|
const raw : any = lib_plankton.json.decode(await lib_plankton.file.read(path));;
|
2024-08-21 15:07:27 +02:00
|
|
|
const version_fallback : int = 1;
|
|
|
|
if (! ("version" in raw)) {
|
|
|
|
lib_plankton.log.warning(
|
|
|
|
"conf_version_unspecified",
|
|
|
|
{
|
|
|
|
"assumed_version": version_fallback,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
const version : int = (raw["version"] ?? version_fallback);
|
|
|
|
switch (version) {
|
|
|
|
default: {
|
|
|
|
throw (new Error("invalid version: " + version.toFixed(0)));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {
|
|
|
|
return {
|
|
|
|
"server": {
|
|
|
|
"host": ((raw["server"] ?? {})["host"] ?? "::"),
|
|
|
|
"port": ((raw["server"] ?? {})["port"] ?? 7463),
|
|
|
|
},
|
|
|
|
"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"),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-08-18 13:57:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
function encode_user_list(
|
2024-08-21 15:07:27 +02:00
|
|
|
user_list : (null | type_user_list_sparse)
|
2024-08-18 13:57:55 +02:00
|
|
|
) : string
|
|
|
|
{
|
|
|
|
let output : string = "";
|
2024-08-21 15:07:27 +02:00
|
|
|
output += "users:\n";
|
|
|
|
Object.entries((user_list === null) ? {} : user_list.users).forEach(
|
2024-08-18 13:57:55 +02:00
|
|
|
([key, value]) => {
|
2024-08-21 15:07:27 +02:00
|
|
|
output += (" " + key + ":\n");
|
|
|
|
output += (" " + "disabled: " + ((value.disabled ?? false) ? "true" : "false") + "\n");
|
|
|
|
output += (" " + "displayname: " + (value.displayname ?? key) + "\n");
|
2024-08-18 13:57:55 +02:00
|
|
|
if ("email" in value) {
|
2024-08-21 15:07:27 +02:00
|
|
|
output += (" " + "email: " + value.email + "\n");
|
2024-08-18 13:57:55 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// do nothing
|
|
|
|
}
|
2024-08-21 15:07:27 +02:00
|
|
|
output += (" " + "groups: " + JSON.stringify(value.groups ?? []) + "\n");
|
|
|
|
output += (" " + "password: " + value.password + "\n");
|
2024-08-18 13:57:55 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
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
|
2024-08-21 19:47:31 +02:00
|
|
|
if (args["help"]) {
|
|
|
|
process.stdout.write(
|
|
|
|
arg_handler.generate_help(
|
|
|
|
{
|
|
|
|
"programname": "arc",
|
|
|
|
"description": "Authelia Remote Control",
|
|
|
|
"executable": "arc",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
+
|
|
|
|
"\n"
|
2024-08-18 13:57:55 +02:00
|
|
|
);
|
2024-08-21 19:47:31 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make(
|
2024-08-18 13:57:55 +02:00
|
|
|
{
|
2024-08-21 19:47:31 +02:00
|
|
|
"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"}
|
2024-08-18 13:57:55 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
2024-08-21 19:47:31 +02:00
|
|
|
{
|
|
|
|
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",
|
|
|
|
});
|
2024-08-18 13:57:55 +02:00
|
|
|
},
|
2024-08-21 19:47:31 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
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),
|
|
|
|
});
|
2024-08-18 13:57:55 +02:00
|
|
|
},
|
2024-08-21 19:47:31 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
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,
|
2024-08-21 15:07:27 +02:00
|
|
|
"type": "object",
|
2024-08-21 19:47:31 +02:00
|
|
|
"additionalProperties": {
|
|
|
|
"nullable": false,
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": false,
|
|
|
|
"properties": {
|
|
|
|
"disabled": {
|
2024-08-21 15:07:27 +02:00
|
|
|
"nullable": false,
|
2024-08-21 19:47:31 +02:00
|
|
|
"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",
|
|
|
|
},
|
2024-08-21 15:07:27 +02:00
|
|
|
},
|
2024-08-21 19:47:31 +02:00
|
|
|
"required": [
|
|
|
|
"password",
|
|
|
|
]
|
2024-08-21 15:07:27 +02:00
|
|
|
},
|
2024-08-21 19:47:31 +02:00
|
|
|
"properties": {},
|
|
|
|
"required": [],
|
2024-08-21 15:07:27 +02:00
|
|
|
}
|
2024-08-21 19:47:31 +02:00
|
|
|
},
|
|
|
|
"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) {
|
2024-08-21 15:07:27 +02:00
|
|
|
lib_plankton.log.notice(
|
2024-08-21 19:47:31 +02:00
|
|
|
"restriction_access_denied_due_to_invalid_timestamp",
|
2024-08-21 15:07:27 +02:00
|
|
|
{
|
2024-08-21 19:47:31 +02:00
|
|
|
"timestamp_local": timestamp_local,
|
|
|
|
"timestamp_remote": timestamp_remote,
|
2024-08-21 15:07:27 +02:00
|
|
|
}
|
|
|
|
);
|
2024-08-18 13:57:55 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
2024-08-21 19:47:31 +02:00
|
|
|
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;
|
2024-08-21 15:07:27 +02:00
|
|
|
}
|
2024-08-21 19:47:31 +02:00
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"execution": async (stuff) => {
|
|
|
|
await lib_plankton.file.write(
|
|
|
|
conf.authelia.usersfile_path,
|
|
|
|
encode_user_list(stuff.input)
|
2024-08-21 15:07:27 +02:00
|
|
|
);
|
2024-08-22 07:45:01 +02:00
|
|
|
lib_plankton.log.info(
|
2024-08-21 19:47:31 +02:00
|
|
|
"userdata_updated",
|
2024-08-21 15:07:27 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
);
|
2024-08-21 19:47:31 +02:00
|
|
|
return Promise.resolve({
|
|
|
|
"status_code": 200,
|
|
|
|
"data": null
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}
|
2024-08-18 13:57:55 +02:00
|
|
|
);
|
|
|
|
}
|
2024-08-21 19:47:31 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2024-08-18 13:57:55 +02:00
|
|
|
|
|
|
|
return Promise.resolve<void>(undefined);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
main(process.argv.slice(2))
|
|
|
|
.then(
|
|
|
|
() => {}
|
|
|
|
)
|
|
|
|
.catch(
|
|
|
|
(error) => {process.stderr.write(String(error));}
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|