This commit is contained in:
Fenris Wolf 2024-09-18 18:17:25 +02:00
parent aea08efed6
commit 9e077fe362
23 changed files with 4156 additions and 5361 deletions

View file

@ -2,5 +2,8 @@
"version": 1, "version": 1,
"log": [ "log": [
{"kind": "stdout", "data": {"threshold": "info"}} {"kind": "stdout", "data": {"threshold": "info"}}
] ],
"session_management": {
"in_memory": false
}
} }

View file

@ -2,120 +2,51 @@
"users": [ "users": [
{ {
"id": 1, "id": 1,
"object": { "name": "alice",
"name": "christian.frass" "email_address": "alice@example.org",
} "password": "alice"
}, },
{ {
"id": 2, "id": 2,
"object": { "name": "bob",
"name": "andre.weichert" "email_address": "bob@example.org",
} "password": "bob"
}, },
{ {
"id": 3, "id": 3,
"object": { "name": "charlie",
"name": "steffen.doegnitz" "email_address": "charlie@example.org",
} "password": "charlie"
},
{
"id": 4,
"object": {
"name": "frank.dietrich"
}
},
{
"id": 5,
"object": {
"name": "michael.berger"
}
},
{
"id": 6,
"object": {
"name": "roland.schroeder"
}
},
{
"id": 7,
"object": {
"name": "rene.hahn"
}
},
{
"id": 8,
"object": {
"name": "max.meierhof"
}
},
{
"id": 9,
"object": {
"name": "klaus.kleba"
}
},
{
"id": 10,
"object": {
"name": "tim.detzner"
}
} }
], ],
"calendars": [ "calendars": [
{ {
"id": 1, "id": 1,
"object": { "name": "house",
"kind": "concrete", "public": false,
"members": [
{
"user_id": 1,
"role": "editor"
}
],
"resource": {
"kind": "local",
"data": { "data": {
"name": "BV",
"private": false,
"hue": 0.0000000000000000,
"users": [
],
"events": [ "events": [
{ {
"name": "9. Bundesparteitag | 1. Sitzung | Tag 1", "name": "clean floors",
"begin": { "begin": {
"timezone_shift": 2, "timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 18}, "date": {"year": 2024, "month": 9, "day": 21},
"time": {"hour": 10, "minute": 0, "second": 0} "time": {"hour": 10, "minute": 0, "second": 0}
}, },
"end": { "end": {
"timezone_shift": 2, "timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 18}, "date": {"year": 2024, "month": 9, "day": 21},
"time": {"hour": 18, "minute": 0, "second": 0} "time": {"hour": 11, "minute": 0, "second": 0}
}, },
"location": "Halle", "location": "1st floor",
"description": null
},
{
"name": "9. Bundesparteitag | 1. Sitzung | Tag 2",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 19},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 19},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"location": "Halle",
"description": null
},
{
"name": "9. Bundesparteitag | 1. Sitzung | Tag 3",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 20},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 20},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"location": "Halle",
"description": null "description": null
} }
] ]
@ -124,73 +55,35 @@
}, },
{ {
"id": 2, "id": 2,
"object": { "name": "turf",
"kind": "concrete", "public": false,
"members": [
{
"user_id": 1,
"role": "viewer"
},
{
"user_id": 2,
"role": "editor"
}
],
"resource": {
"kind": "local",
"data": { "data": {
"name": "LV Sachsen",
"private": false,
"hue": 0.6180339887498949,
"users": [
{
"id": 9,
"role": "editor"
}
],
"events": [ "events": [
{ {
"name": "Sören Pellmann zu den Landtagswahlen im Osten", "name": "garden party",
"begin": { "begin": {
"timezone_shift": 2, "timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 11}, "date": {"year": 2024, "month": 9, "day": 21},
"time": {"hour": 18, "minute": 0, "second": 0} "time": {"hour": 18, "minute": 0, "second": 0}
}, },
"end": null,
"location": "online: https://v2202002113208108062.supersrv.de/b/har-jbu-lxy-rx1",
"description": null
},
{
"name": "Erinnern versammeln. Praktiken für die Zukünfte einer Gesellschaft der Vielen.",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 13},
"time": {"hour": 17, "minute": 0, "second": 0}
},
"end": { "end": {
"timezone_shift": 2, "timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 15}, "date": {"year": 2024, "month": 9, "day": 21},
"time": null "time": {"hour": 23, "minute": 0, "second": 0}
}, },
"location": "Weltecho, Annaberger Straße 24, 09111 Chemnitz", "location": "bob's garden",
"description": null
},
{
"name": "Parteikonvent zur Auswertung des Wahljahres",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 14},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 14},
"time": {"hour": 16, "minute": 30, "second": 0}
},
"location": "Veranstaltungs- und Kulturforum STADTPARK | Hammertal 3 | 09669 Frankenberg/Sachsen",
"description": null
},
{
"name": "Ist die extreme Rechte noch zu stoppen?",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 19, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 21, "minute": 0, "second": 0}
},
"location": "online: https://www.dielinke-sachsen.de/termine/?termin_ort=digital-internet-stream",
"description": null "description": null
} }
] ]
@ -199,149 +92,44 @@
}, },
{ {
"id": 3, "id": 3,
"object": { "name": "town",
"kind": "concrete", "public": true,
"data": { "members": [
"name": "KV Zwickau", {
"private": false, "user_id": 1,
"hue": 0.4721359549995796, "role": "viewer"
"events": [ },
{ {
"name": "Vorstands-Sitzung", "user_id": 2,
"begin": { "role": "viewer"
"timezone_shift": 2, },
"date": {"year": 2024, "month": 9, "day": 5}, {
"time": {"hour": 18, "minute": 0, "second": 0} "user_id": 3,
}, "role": "editor"
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 5},
"time": {"hour": 21, "minute": 0, "second": 0}
},
"location": "Zwickau, Innere Schneeberger Straße 17",
"description": null
}
]
} }
} ],
}, "resource": {
{ "kind": "local",
"id": 4,
"object": {
"kind": "concrete",
"data": { "data": {
"name": "OV Glauchau",
"private": false,
"users": [
{
"id": 1,
"role": "editor"
},
{
"id": 5,
"role": "editor"
},
{
"id": 6,
"role": "editor"
}
],
"hue": 0.09016994374947451,
"events": [ "events": [
{ {
"name": "Kinderspieletag", "name": "ting",
"begin": { "begin": {
"timezone_shift": 2, "timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 8}, "date": {"year": 2024, "month": 9, "day": 23},
"time": {"hour": 12, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 8},
"time": {"hour": 16, "minute": 0, "second": 0} "time": {"hour": 16, "minute": 0, "second": 0}
}, },
"location": null,
"description": null
},
{
"name": "Mitglieder-Sitzung",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 17, "minute": 30, "second": 0}
},
"end": { "end": {
"timezone_shift": 2, "timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19}, "date": {"year": 2024, "month": 9, "day": 23},
"time": {"hour": 19, "minute": 0, "second": 0} "time": {"hour": 18, "minute": 0, "second": 0}
}, },
"location": null, "location": "market square",
"description": null "description": null
} }
] ]
} }
} }
},
{
"id": 5,
"object": {
"kind": "concrete",
"data": {
"name": "OV Zwickau",
"private": false,
"hue": 0.09016994374947451,
"users": [
{
"id": 7,
"role": "editor"
},
{
"id": 8,
"role": "viewer"
}
],
"events": [
{
"name": "4ter Christopher Street Day",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 8, "day": 31},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 8, "day": 31},
"time": {"hour": 17, "minute": 0, "second": 0}
},
"location": "Zwickau",
"description": null
},
{
"name": "Regionaltreffen Westsachsen: Schule ohne Rassismus Schule mit Courage",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 9, "minute": 0, "second": 0}
},
"end": null,
"location": null,
"description": null
}
]
}
}
},
{
"id": 6,
"object": {
"kind": "caldav",
"data": {
"name": "Lixer",
"private": true,
"url": "https://export.kalender.digital/ics/0/3e10dae66950379d4cc8/gesamterkalender.ics?past_months=3&future_months=36",
"read_only": true
}
}
} }
] ]
} }

347
data/linke.json Normal file
View file

@ -0,0 +1,347 @@
{
"users": [
{
"id": 1,
"object": {
"name": "christian.frass"
}
},
{
"id": 2,
"object": {
"name": "andre.weichert"
}
},
{
"id": 3,
"object": {
"name": "steffen.doegnitz"
}
},
{
"id": 4,
"object": {
"name": "frank.dietrich"
}
},
{
"id": 5,
"object": {
"name": "michael.berger"
}
},
{
"id": 6,
"object": {
"name": "roland.schroeder"
}
},
{
"id": 7,
"object": {
"name": "rene.hahn"
}
},
{
"id": 8,
"object": {
"name": "max.meierhof"
}
},
{
"id": 9,
"object": {
"name": "klaus.kleba"
}
},
{
"id": 10,
"object": {
"name": "tim.detzner"
}
}
],
"calendars": [
{
"id": 1,
"object": {
"kind": "concrete",
"data": {
"name": "BV",
"private": false,
"hue": 0.0000000000000000,
"users": [
],
"events": [
{
"name": "9. Bundesparteitag | 1. Sitzung | Tag 1",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 18},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 18},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"location": "Halle",
"description": null
},
{
"name": "9. Bundesparteitag | 1. Sitzung | Tag 2",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 19},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 19},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"location": "Halle",
"description": null
},
{
"name": "9. Bundesparteitag | 1. Sitzung | Tag 3",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 20},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 10, "day": 20},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"location": "Halle",
"description": null
}
]
}
}
},
{
"id": 2,
"object": {
"kind": "concrete",
"data": {
"name": "LV Sachsen",
"private": false,
"hue": 0.6180339887498949,
"users": [
{
"id": 9,
"role": "editor"
}
],
"events": [
{
"name": "Sören Pellmann zu den Landtagswahlen im Osten",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 11},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"end": null,
"location": "online: https://v2202002113208108062.supersrv.de/b/har-jbu-lxy-rx1",
"description": null
},
{
"name": "Erinnern versammeln. Praktiken für die Zukünfte einer Gesellschaft der Vielen.",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 13},
"time": {"hour": 17, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 15},
"time": null
},
"location": "Weltecho, Annaberger Straße 24, 09111 Chemnitz",
"description": null
},
{
"name": "Parteikonvent zur Auswertung des Wahljahres",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 14},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 14},
"time": {"hour": 16, "minute": 30, "second": 0}
},
"location": "Veranstaltungs- und Kulturforum STADTPARK | Hammertal 3 | 09669 Frankenberg/Sachsen",
"description": null
},
{
"name": "Ist die extreme Rechte noch zu stoppen?",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 19, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 21, "minute": 0, "second": 0}
},
"location": "online: https://www.dielinke-sachsen.de/termine/?termin_ort=digital-internet-stream",
"description": null
}
]
}
}
},
{
"id": 3,
"object": {
"kind": "concrete",
"data": {
"name": "KV Zwickau",
"private": false,
"hue": 0.4721359549995796,
"events": [
{
"name": "Vorstands-Sitzung",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 5},
"time": {"hour": 18, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 5},
"time": {"hour": 21, "minute": 0, "second": 0}
},
"location": "Zwickau, Innere Schneeberger Straße 17",
"description": null
}
]
}
}
},
{
"id": 4,
"object": {
"kind": "concrete",
"data": {
"name": "OV Glauchau",
"private": false,
"users": [
{
"id": 1,
"role": "editor"
},
{
"id": 5,
"role": "editor"
},
{
"id": 6,
"role": "editor"
}
],
"hue": 0.09016994374947451,
"events": [
{
"name": "Kinderspieletag",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 8},
"time": {"hour": 12, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 8},
"time": {"hour": 16, "minute": 0, "second": 0}
},
"location": null,
"description": null
},
{
"name": "Mitglieder-Sitzung",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 17, "minute": 30, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 19, "minute": 0, "second": 0}
},
"location": null,
"description": null
}
]
}
}
},
{
"id": 5,
"object": {
"kind": "concrete",
"data": {
"name": "OV Zwickau",
"private": false,
"hue": 0.09016994374947451,
"users": [
{
"id": 7,
"role": "editor"
},
{
"id": 8,
"role": "viewer"
}
],
"events": [
{
"name": "4ter Christopher Street Day",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 8, "day": 31},
"time": {"hour": 10, "minute": 0, "second": 0}
},
"end": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 8, "day": 31},
"time": {"hour": 17, "minute": 0, "second": 0}
},
"location": "Zwickau",
"description": null
},
{
"name": "Regionaltreffen Westsachsen: Schule ohne Rassismus Schule mit Courage",
"begin": {
"timezone_shift": 2,
"date": {"year": 2024, "month": 9, "day": 19},
"time": {"hour": 9, "minute": 0, "second": 0}
},
"end": null,
"location": null,
"description": null
}
]
}
}
},
{
"id": 6,
"object": {
"kind": "caldav",
"data": {
"name": "Lixer",
"private": true,
"url": "https://export.kalender.digital/ics/0/3e10dae66950379d4cc8/gesamterkalender.ics?past_months=3&future_months=36",
"read_only": true
}
}
}
]
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -58,16 +58,21 @@ namespace _zeitbild.api
], ],
} }
}), }),
"restriction": restriction_none, // TODO "restriction": restriction_logged_in,
"execution": () => ( "execution": async (stuff) => {
_zeitbild.service.calendar.overview(2) // TODO: user_id const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
.then( const user_id : _zeitbild.type.user_id = await _zeitbild.service.user.identify(session.value.name);
data => Promise.resolve({
"status_code": 200, return (
"data": data, _zeitbild.service.calendar.overview(user_id)
}) .then(
) data => Promise.resolve({
) "status_code": 200,
"data": data,
})
)
);
}
} }
); );
} }

View file

@ -102,6 +102,9 @@ namespace _zeitbild.api
}), }),
"restriction": restriction_none, // TODO "restriction": restriction_none, // TODO
"execution": async (stuff) => { "execution": async (stuff) => {
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
const user_id : _zeitbild.type.user_id = await _zeitbild.service.user.identify(session.value.name);
const from : _zeitbild.helpers.type_pit = parseInt(stuff.query_parameters["from"]); const from : _zeitbild.helpers.type_pit = parseInt(stuff.query_parameters["from"]);
const to : _zeitbild.helpers.type_pit = parseInt(stuff.query_parameters["to"]); const to : _zeitbild.helpers.type_pit = parseInt(stuff.query_parameters["to"]);
const calendar_ids_wanted : Array<_zeitbild.type.calendar_id> = ( const calendar_ids_wanted : Array<_zeitbild.type.calendar_id> = (
@ -123,7 +126,7 @@ namespace _zeitbild.api
null null
); );
const calendar_ids_allowed : Array<_zeitbild.type.calendar_id> = ( const calendar_ids_allowed : Array<_zeitbild.type.calendar_id> = (
(await _zeitbild.service.calendar.overview(0)) // TODO: user_id (await _zeitbild.service.calendar.overview(user_id))
.map((x : any) => x.id) .map((x : any) => x.id)
); );
const calendar_ids : Array<_zeitbild.type.calendar_id> = ( const calendar_ids : Array<_zeitbild.type.calendar_id> = (

View file

@ -50,15 +50,15 @@ namespace _zeitbild.api
return Promise.reject(new Error("impossible")); return Promise.reject(new Error("impossible"));
} }
else { else {
const admin : (null | _zeitbild.service.admin.type_value) = await _zeitbild.service.admin.login(input.name, input.password); const passed : boolean = await _zeitbild.service.auth_internal.check(input.name, input.password);
if (admin === null) { if (! passed) {
return Promise.resolve({ return Promise.resolve({
"status_code": 403, "status_code": 403,
"data": null, "data": null,
}); });
} }
else { else {
const session_key : string = await lib_plankton.session.begin(admin.name); const session_key : string = await lib_plankton.session.begin(input.name);
return Promise.resolve({ return Promise.resolve({
"status_code": 201, "status_code": 201,
"data": session_key, "data": session_key,

View file

@ -13,7 +13,7 @@ namespace _zeitbild.api
lib_plankton.http.enum_method.delete, lib_plankton.http.enum_method.delete,
"/session/oidc", "/session/oidc",
{ {
"description": "beendet eine Sitzung", "description": "verarbeitet einen OIDC login callback",
"input_schema": () => ({ "input_schema": () => ({
"type": "null", "type": "null",
}), }),
@ -22,8 +22,15 @@ namespace _zeitbild.api
}), }),
"restriction": restriction_logged_in, "restriction": restriction_logged_in,
"execution": async (stuff) => { "execution": async (stuff) => {
const session : {key : string; value : lib_plankton.session.type_session} = await session_from_stuff(stuff); // do NOT wait
await lib_plankton.session.end(session.key); _zeitbild.auth.control(
{
"kind": "authorization_callback",
"data": {
"http_request": http_request
}
}
);
return Promise.resolve({ return Promise.resolve({
"status_code": 200, "status_code": 200,
"data": null, "data": null,

View file

@ -13,11 +13,10 @@ namespace _zeitbild.api
name : string; name : string;
password : string; password : string;
}, },
( {
null kind : string;
| data : any;
string }
)
>( >(
rest_subject, rest_subject,
lib_plankton.http.enum_method.get, lib_plankton.http.enum_method.get,
@ -28,25 +27,15 @@ namespace _zeitbild.api
"nullable": true, "nullable": true,
}), }),
"output_schema": () => ({ "output_schema": () => ({
"type": "string", "nullable": false
"description": "der Sitzungs-Schlüssel, der als Header 'X-Session-Key' gesetzt werden muss um Erlaubnis zur Ausführung geschützter Aktionen zu erhalten",
}), }),
"restriction": restriction_none, "restriction": restriction_none,
"execution": async () => { "execution": async () => {
const admin : (null | _zeitbild.service.admin.type_value) = await _zeitbild.service.admin.login(input.name, input.password); const preparation = await _zeitbild.auth.prepare();
if (admin === null) { return Promise.resolve({
return Promise.resolve({ "status_code": 200,
"status_code": 403, "data": preparation,
"data": null, });
});
}
else {
const session_key : string = await lib_plankton.session.begin(admin.name);
return Promise.resolve({
"status_code": 201,
"data": session_key,
});
}
}, },
} }
); );

View file

@ -26,8 +26,10 @@ namespace _zeitbild.api
} }
// session // session
{ {
// _zeitbild.api.register_session_prepare(rest_subject);
_zeitbild.api.register_session_begin(rest_subject); _zeitbild.api.register_session_begin(rest_subject);
_zeitbild.api.register_session_end(rest_subject); _zeitbild.api.register_session_end(rest_subject);
// _zeitbild.api.register_session_oidc(rest_subject);
} }
// calendar // calendar
{ {

93
source/auth.ts Normal file
View file

@ -0,0 +1,93 @@
namespace _zeitbild.auth
{
/**
let _subject : (
null
|
lib_plankton.auth.type_auth<any, any>
) = null;
*/
/**
*/
export function init(
) : Promise<void>
{
/*
switch (_zeitbild.conf.get().auth.kind) {
case "internal": {
_subject = lib_plankton.auth.internal.implementation_auth(
{
// "password_image_chest": password_image_chest,
// "check_password": (image, input) => Promise.resolve<boolean>(image === get_password_image(input)),
}
);
}
case "oidc": {
_subject = lib_plankton.auth.oidc.implementation_auth(
{
"url_authorization": _zeitbild.conf.get().auth.data.url_authorization,
"url_token": _zeitbild.conf.get().auth.data.url_token,
"url_userinfo": _zeitbild.conf.get().auth.data.url_userinfo,
"client_id": _zeitbild.conf.get().auth.data.client_id,
"client_secret": _zeitbild.conf.get().auth.data.client_secret,
"url_redirect": "/session/begin",
"scopes": [
"openid",
"profile",
"email",
],
"login_url_mode": "open",
}
);
}
default: {
// do nothing
break;
}
}
*/
return Promise.resolve<void>(undefined);
}
/**
export function prepare(
) : Promise<{kind : string; data : any;}>
{
return (
_subject.prepare()
.then(
(data) => ({
"kind": _zeitbild.conf.get().auth.kind,
"data": data,
})
)
);
}
*/
/**
export function execute(
input : any
) : Promise<string>
{
return _subject.execute(input);
}
*/
/**
export function control(
input : any
) : Promise<void>
{
return _subject.control(input);
}
*/
}

View file

@ -4,245 +4,214 @@ namespace _zeitbild.conf
/** /**
*/ */
type type_log_threshold = ( const _schema : lib_plankton.conf.type_schema = {
"debug" "nullable": false,
| "type": "object",
"info" "properties": {
| "version": {
"notice" "nullable": false,
| "type": "integer",
"warning" "enum": [1]
| },
"error" "log": {
); "nullable": false,
"type": "array",
"items": {
/** "anyOf": [
*/ {
type type_log_format = ( "type": "object",
"jsonl" "properties": {
| "kind": {
"human_readable" "nullable": false,
); "type": "string",
"enum": ["stdout"]
},
/** "data": {
*/ "nullable": false,
export type type_conf = { "type": "object",
general : { "properties": {
language : (null | string); "threshold": {
}; "nullable": false,
log : Array< "type": "string",
{ "enum": [
kind : "stdout"; "debug",
data : { "info",
threshold : type_log_threshold; "notice",
}; "warning",
"error"
],
"default": "info"
}
},
"required": [
]
}
}
}
]
},
"default": [
{
"kind": "stdout",
"data": {
"threshold": "info"
}
}
]
},
"server": {
"nullable": false,
"type": "object",
"properties": {
"address": {
"nullable": false,
"type": "string",
"default": "::"
},
"port": {
"nullable": false,
"type": "integer",
"default": 7845
}
},
"required": [
],
"additionalProperties": false,
"default": {
}
},
"database": {
"anyOf": [
{
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["sqlite"]
},
"data": {
"nullable": false,
"type": "object",
"properties": {
"path": {
"nullable": false,
"type": "string",
"default": "data.sqlite"
}
},
"required": [
],
"default": {}
}
},
"required": [
"kind"
]
},
{
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["postgresql"]
},
"data": {
"nullable": false,
"type": "object",
"properties": {
"host": {
"nullable": false,
"type": "string"
},
"port": {
"nullable": false,
"type": "integer",
"default": 5432
},
"username": {
"nullable": false,
"type": "string"
},
"password": {
"nullable": false,
"type": "string"
},
"scheme": {
"nullable": false,
"type": "string"
}
},
"required": [
"host",
"username",
"password",
"scheme"
]
}
},
"required": [
"kind",
"data"
]
}
],
"default": {
"kind": "sqlite"
}
},
"session_management": {
"nullable": false,
"type": "object",
"properties": {
"in_memory": {
"nullable": false,
"type": "boolean",
"default": true
},
"drop_all_at_start": {
"nullable": false,
"type": "boolean",
"default": true
},
"lifetime": {
"nullable": false,
"type": "integer",
"default": 900
}
},
"required": [
],
"additionalProperties": false,
"default": {}
} }
| },
{ "required": [
kind : "file"; "version"
data : { ],
threshold : type_log_threshold; "additionalProperties": false
path : string;
};
}
|
{
kind : "email";
data : {
threshold : type_log_threshold;
smtp_credentials : {
host : string;
port : int;
username : string;
password : string;
};
sender : string;
receivers : Array<string>;
};
}
>;
server : {
host : string;
port : int;
path_base : string;
};
database : (
{
kind : "sqlite";
data : {
path : string;
};
}
|
{
kind : "postgresql";
data : {
host : string;
port ?: int;
username : string;
password : string;
schema : string;
};
}
);
authentication : (
{
kind : "internal";
data : {
};
}
|
{
kind : "oidc";
data : {
client_id : string;
client_secret : string;
url_authorization : string;
url_token : string;
url_userinfo : string;
};
}
);
session_management : {
in_memory : boolean;
drop_all_at_start : boolean;
lifetime : int;
};
}; };
/** /**
*/ */
var _data : (null | type_conf) = null; var _data : (null | any) = null;
/** /**
*/ */
export function inject( export function schema(
conf_raw : any ) : lib_plankton.conf.type_schema
) : void
{ {
const version : int = (conf_raw["version"] ?? 1); return _schema;
_data = {
"general": (
((node_general) => ({
"language": (node_general["language"] ?? null),
})) (conf_raw["general"] ?? {})
),
"log": (
(() => {
const node_log = (
conf_raw["log"]
??
[
{
"kind": "stdout",
"data": {
}
},
]
);
return (
node_log.map(
(node_log_entry : any) => ({
"kind": node_log_entry["kind"],
"data": Object.assign(
{
"format": "human_readable",
"threshold": "notice",
},
(node_log_entry["data"] ?? {})
)
})
)
);
}) ()
),
"server": (
((node_server) => ({
"host": (() => {
return (node_server["host"] ?? "::");
}) (),
"port": (node_server["port"] ?? 7845),
"path_base": (node_server["path_base"] ?? ""),
})) (conf_raw["server"] ?? {})
),
"database": (
((node_database) => {
const kind : string = (node_database["kind"] ?? "sqlite");
const node_database_data_raw = (node_database["data"] ?? {});
switch (kind) {
case "sqlite": {
return {
"kind": kind,
"data": {
"path": (node_database_data_raw["path"] ?? "data.sqlite"),
}
};
break;
}
case "postgresql": {
return {
"kind": kind,
"data": node_database_data_raw,
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["database"] ?? {})
),
"session_management": (
((node_session_management) => ({
"in_memory": (node_session_management["in_memory"] ?? true),
"drop_all_at_start": (node_session_management["drop_all_at_start"] ?? true),
"lifetime": (node_session_management["lifetime"] ?? 900),
})) (conf_raw["session_management"] ?? {})
),
};
}
/**
* @todo mandatory fields
*/
export async function load(
path : string
) : Promise<void>
{
let conf_raw : any;
if (! (await lib_plankton.file.exists(path))) {
// return Promise.reject<void>(new Error("configuration file not found: " + path + "; using fallback"));
conf_raw = {};
}
else {
try {
conf_raw = lib_plankton.json.decode(await lib_plankton.file.read(path));
}
catch (error) {
conf_raw = null;
}
}
if (conf_raw === null) {
return Promise.reject<void>("configuration file could not be read");
}
else {
inject(conf_raw);
// process.stderr.write(JSON.stringify(_data, undefined, "\t"));
return Promise.resolve<void>(undefined);
}
} }
/** /**
*/ */
export function get( export function get(
) : type_conf ) : any
{ {
if (_data === null) { if (_data === null) {
throw (new Error("conf not loaded yet")); throw (new Error("conf not loaded yet"));
@ -252,4 +221,18 @@ namespace _zeitbild.conf
} }
} }
/**
*/
export async function init(
path : string
) : Promise<void>
{
_data = await lib_plankton.conf.load(
_schema,
path
);
return Promise.resolve<void>(undefined);
}
} }

View file

@ -1,4 +1,90 @@
/**
*/
type type_data = {
users : Array<
{
id : int;
name : string;
email_address : string;
password : string;
}
>;
calendars : Array<
{
id : int;
name : string;
public : boolean;
members : Array<
{
user_id : int;
role : _zeitbild.type.role;
}
>;
resource : _zeitbild.type.resource_object;
}
>;
};
/**
*/
async function data_init(
data : type_data
) : Promise<void>
{
let track : {
user : Record<
int,
_zeitbild.type.user_id
>;
calendar : Record<
int,
_zeitbild.type.user_id
>;
} = {
"user": {},
"calendar": {},
};
for await (const user_raw of data.users) {
const user_id : _zeitbild.type.user_id = await _zeitbild.service.user.add(
{
"name": user_raw.name,
"email_address": user_raw.email_address,
}
);
await _zeitbild.service.auth_internal.set(
user_raw.name,
user_raw.password
);
track.user[user_raw.id] = user_id;
}
for await (const calendar_raw of data.calendars) {
const resource_id : _zeitbild.type.resource_id = await _zeitbild.service.resource.add(
calendar_raw.resource
);
const calendar_id : _zeitbild.type.calendar_id = await _zeitbild.service.calendar.add(
{
"name": calendar_raw.name,
"public": calendar_raw.public,
"members": (
calendar_raw.members
.map(
(member_raw) => ({
"user_id": track.user[member_raw.user_id],
"role": member_raw.role,
})
)
),
"resource_id": resource_id,
}
);
track.calendar[calendar_raw.id] = calendar_id;
}
return Promise.resolve<void>(undefined);
}
/** /**
*/ */
async function main( async function main(
@ -35,8 +121,16 @@ async function main(
"description": "api-doc" "description": "api-doc"
}, },
{ {
"name": "expose-conf", "name": "conf-schema",
"description": "expose-conf" "description": "conf-schema"
},
{
"name": "conf-expose",
"description": "conf-expose"
},
{
"name": "fill",
"description": "fill"
}, },
{ {
"name": "help", "name": "help",
@ -88,10 +182,12 @@ async function main(
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" ")); const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
// init2 // init2
await _zeitbild.conf.load(args["conf_path"]); await _zeitbild.conf.init(
args["conf_path"]
);
lib_plankton.log.conf_push( lib_plankton.log.conf_push(
_zeitbild.conf.get().log.map( _zeitbild.conf.get().log.map(
log_output => lib_plankton.log.channel_make( (log_output : any) => lib_plankton.log.channel_make(
{ {
"kind": log_output.kind, "kind": log_output.kind,
"data": log_output.data "data": log_output.data
@ -125,7 +221,19 @@ async function main(
); );
break; break;
} }
case "expose-conf": { case "conf-schema": {
process.stdout.write(
JSON.stringify(
_zeitbild.conf.schema(),
undefined,
"\t"
)
+
"\n"
);
break;
}
case "conf-expose": {
process.stdout.write( process.stdout.write(
JSON.stringify( JSON.stringify(
_zeitbild.conf.get(), _zeitbild.conf.get(),
@ -150,6 +258,15 @@ async function main(
); );
break; break;
} }
case "fill": {
await data_init(
lib_plankton.json.decode(
await lib_plankton.file.read(args.data_path)
)
);
process.stdout.write("-- done\n");
break;
}
case "serve": { case "serve": {
// prepare database // prepare database
await _zeitbild.database.check(); await _zeitbild.database.check();
@ -184,6 +301,8 @@ async function main(
} }
); );
await _zeitbild.auth.init();
const rest_subject : lib_plankton.rest.type_rest = _zeitbild.api.make(); const rest_subject : lib_plankton.rest.type_rest = _zeitbild.api.make();
const server : lib_plankton.server.type_subject = lib_plankton.server.make( const server : lib_plankton.server.type_subject = lib_plankton.server.make(
async (input, metadata) => { async (input, metadata) => {
@ -219,9 +338,12 @@ async function main(
( (
main(process.argv.slice(2)) main(process.argv.slice(2))
.then( .then(
() => {} () => {
}
) )
.catch( .catch(
(error) => {process.stderr.write(String(error) + "\n");} (error) => {
process.stderr.write(String(error) + "\n");
}
) )
); );

View file

@ -0,0 +1,77 @@
namespace _zeitbild.repository.auth_internal
{
/**
*/
var _chest : (
null
|
lib_plankton.storage.type_chest<
Array<string>,
Record<string, any>,
lib_plankton.database.type_description_create_table,
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
Record<string, any>
>
) = null;
/**
*/
function get_chest(
) : lib_plankton.storage.type_chest<
Array<string>,
Record<string, any>,
lib_plankton.database.type_description_create_table,
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
Record<string, any>
>
{
if (_chest === null) {
_chest = lib_plankton.storage.sql_table_common.chest(
{
"database_implementation": _zeitbild.database.get_implementation(),
"table_name": "auth_internal",
"key_names": ["name"],
}
);
}
else {
// do nothing
}
return _chest;
}
/**
*/
export function read(
name : string
) : Promise<string>
{
return (
get_chest().read([name])
.then(
(row) => Promise.resolve<string>(row["password_image"])
)
);
}
/**
*/
export function write(
name : string,
password_image : string
) : Promise<void>
{
return (
get_chest().write([name], {"password_image": password_image})
.then(
() => Promise.resolve<void>(undefined)
)
);
}
}

View file

@ -210,6 +210,92 @@ namespace _zeitbild.repository.resource
*/ */
/**
*/
function encode_event(
event : _zeitbild.type.event_object,
local_resource_id : int
) : Record<string, any>
{
/*
const decode_datetime : ((datetime_raw : string) => _zeitbild.helpers.type_datetime) = ((datetime_raw) => {
const parts : Array<string> = datetime_raw.split("|");
const timezone_shift : int = parseInt(parts[0]);
if (parts[1].length <= 10) {
return {
"timezone_shift": timezone_shift,
"date": {
"year": parseInt(parts[1].slice(0, 4)),
"month": parseInt(parts[1].slice(5, 7)),
"day": parseInt(parts[1].slice(8, 10)),
},
"time": null
};
}
else {
return {
"timezone_shift": timezone_shift,
"date": {
"year": parseInt(parts[1].slice(0, 4)),
"month": parseInt(parts[1].slice(5, 7)),
"day": parseInt(parts[1].slice(8, 10)),
},
"time": {
"hour": parseInt(parts[1].slice(11, 13)),
"minute": parseInt(parts[1].slice(14, 16)),
"second": parseInt(parts[1].slice(17, 19)),
}
};
}
});
*/
const encode_datetime : ((datetime : _zeitbild.helpers.type_datetime) => string) = ((datetime) => {
return lib_plankton.string.coin(
"{{timezone_shift}}|{{date}}{{macro_time}}",
{
"timezone_shift": datetime.timezone_shift.toFixed(0).padStart(2, "0"),
"date": lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": datetime.date.year.toFixed(0).padStart(4, "0"),
"month": datetime.date.month.toFixed(0).padStart(2, "0"),
"day": datetime.date.day.toFixed(0).padStart(2, "0"),
}
),
"macro_time": (
(datetime.time === null)
?
""
:
lib_plankton.string.coin(
"T{{hour}}:{{minute}}:{{second}}",
{
"hour": datetime.time.hour.toFixed(0).padStart(2, "0"),
"minute": datetime.time.minute.toFixed(0).padStart(2, "0"),
"second": datetime.time.second.toFixed(0).padStart(2, "0"),
}
)
),
}
);
});
return {
"local_resource_id": local_resource_id,
"name": event.name,
"begin": encode_datetime(event.begin),
"end": (
(event.end === null)
?
null
:
encode_datetime(event.end)
),
"location": event.location,
"description": event.description,
}
}
/** /**
*/ */
function decode_event( function decode_event(
@ -311,4 +397,58 @@ namespace _zeitbild.repository.resource
} }
} }
/**
*/
export async function create(
resource_object : _zeitbild.type.resource_object
) : Promise<_zeitbild.type.resource_id>
{
switch (resource_object.kind) {
case "local": {
const local_resource_id : int = await get_local_resource_core_store().create(
{
"_dummy": null,
}
);
for await (const event of resource_object.data.events) {
get_local_resource_event_store().create(
encode_event(
event,
local_resource_id
)
)
}
const resource_id : _zeitbild.type.resource_id = await get_resource_core_store().create(
{
"kind": "local",
"sub_id": local_resource_id,
}
);
return Promise.resolve<_zeitbild.type.resource_id>(resource_id);
break;
}
case "caldav": {
const caldav_resource_id : int = await get_caldav_resource_store().create(
{
"url": resource_object.data.url,
"read_only": resource_object.data.read_only,
}
);
const resource_id : _zeitbild.type.resource_id = await get_local_resource_core_store().create(
{
"kind": "caldav",
"sub_id": caldav_resource_id,
}
);
return Promise.resolve<_zeitbild.type.resource_id>(resource_id);
break;
}
default: {
throw (new Error("not implemended"));
break;
}
}
}
} }

119
source/repositories/user.ts Normal file
View file

@ -0,0 +1,119 @@
namespace _zeitbild.repository.user
{
/**
*/
var _store : (
null
|
lib_plankton.storage.type_store<
_zeitbild.type.user_id,
Record<string, any>,
{},
lib_plankton.storage.type_sql_table_autokey_search_term,
Record<string, any>
>
) = null;
/**
*/
function get_store(
) : lib_plankton.storage.type_store<
_zeitbild.type.user_id,
Record<string, any>,
{},
lib_plankton.storage.type_sql_table_autokey_search_term,
Record<string, any>
>
{
if (_store === null) {
_store = lib_plankton.storage.sql_table_autokey_store(
{
"database_implementation": _zeitbild.database.get_implementation(),
"table_name": "users",
"key_name": "id",
}
);
}
else {
// do nothing
}
return _store;
}
/**
*/
function encode(
user_object : _zeitbild.type.user_object
) : Record<string, any>
{
return {
"name": user_object.name,
"email_address": user_object.email_address,
};
}
/**
*/
function decode(
row : Record<string, any>
) : _zeitbild.type.user_object
{
return {
"name": row["name"],
"email_address": row["email_address"],
};
}
/**
*/
export async function read(
user_id : _zeitbild.type.user_id
) : Promise<_zeitbild.type.user_object>
{
const row : Record<string, any> = await get_store().read(user_id);
const user_object : _zeitbild.type.user_object = decode(row);
return Promise.resolve<_zeitbild.type.user_object>(user_object);
}
/**
*/
export async function create(
user_object : _zeitbild.type.user_object
) : Promise<_zeitbild.type.user_id>
{
const row : Record<string, any> = encode(user_object);
const user_id : _zeitbild.type.user_id = await get_store().create(row);
return Promise.resolve<_zeitbild.type.user_id>(user_id);
}
/**
*/
export async function identify(
name : string
) : Promise<_zeitbild.type.user_id>
{
const hits : Array<{key : _zeitbild.type.user_id; preview : any;}> = await get_store().search(
{
"expression": "(name = $name)",
"arguments": {
"name": name,
}
}
);
if (hits.length <= 0) {
return Promise.reject<_zeitbild.type.user_id>(new Error("not found"));
}
else {
return Promise.resolve<_zeitbild.type.user_id>(hits[0].key);
}
}
}

View file

@ -0,0 +1,40 @@
namespace _zeitbild.service.auth_internal
{
/**
*/
export function set(
name : string,
password : string
) : Promise<void>
{
return (
lib_plankton.bcrypt.compute(password)
.then<void>(
(password_image) => _zeitbild.repository.auth_internal.write(name, password_image)
)
);
}
/**
*/
export async function check(
name : string,
password : string
) : Promise<boolean>
{
try {
const password_image : string = await _zeitbild.repository.auth_internal.read(name);
return lib_plankton.bcrypt.compare(
password_image,
password
);
}
catch (error) {
return Promise.resolve<boolean>(false);
}
}
}

View file

@ -2,6 +2,16 @@
namespace _zeitbild.service.calendar namespace _zeitbild.service.calendar
{ {
/**
*/
export function add(
calendar_object : _zeitbild.type.calendar_object
) : Promise<_zeitbild.type.calendar_id>
{
return _zeitbild.repository.calendar.create(calendar_object);
}
/** /**
*/ */
export async function list( export async function list(

View file

@ -0,0 +1,14 @@
namespace _zeitbild.service.resource
{
/**
*/
export function add(
resource_object : _zeitbild.type.resource_object
) : Promise<_zeitbild.type.resource_id>
{
return _zeitbild.repository.resource.create(resource_object);
}
}

24
source/services/user.ts Normal file
View file

@ -0,0 +1,24 @@
namespace _zeitbild.service.user
{
/**
*/
export function add(
user_object : _zeitbild.type.user_object
) : Promise<_zeitbild.type.user_id>
{
return _zeitbild.repository.user.create(user_object);
}
/**
*/
export function identify(
name : string
) : Promise<_zeitbild.type.user_id>
{
return _zeitbild.repository.user.identify(name);
}
}

View file

@ -18,16 +18,32 @@ cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc
## rules ## rules
.PHONY: default .PHONY: default
default: ${dir_build}/zeitbild default: ${dir_build}/zeitbild node_modules
.PHONY: node_modules
node_modules:
@ cd ${dir_build} && npm install sqlite3 bcrypt
${dir_temp}/conf.ts: \
${dir_source}/conf.ts.tpl \
${dir_source}/conf.schema.json
@ ${cmd_mkdir} $(dir $@)
${dir_tools}/coin $@
${dir_temp}/zeitbild-unlinked.js: \ ${dir_temp}/zeitbild-unlinked.js: \
${dir_lib}/plankton/plankton.d.ts \ ${dir_lib}/plankton/plankton.d.ts \
${dir_source}/helpers.ts \ ${dir_source}/helpers.ts \
${dir_source}/conf.ts \ ${dir_source}/conf.ts \
${dir_source}/database.ts \ ${dir_source}/database.ts \
${dir_source}/auth.ts \
${dir_source}/types.ts \ ${dir_source}/types.ts \
${dir_source}/repositories/auth_internal.ts \
${dir_source}/repositories/user.ts \
${dir_source}/repositories/resource.ts \ ${dir_source}/repositories/resource.ts \
${dir_source}/repositories/calendar.ts \ ${dir_source}/repositories/calendar.ts \
${dir_source}/services/auth_internal.ts \
${dir_source}/services/user.ts \
${dir_source}/services/resource.ts \
${dir_source}/services/calendar.ts \ ${dir_source}/services/calendar.ts \
${dir_source}/api/base.ts \ ${dir_source}/api/base.ts \
${dir_source}/api/actions/meta_ping.ts \ ${dir_source}/api/actions/meta_ping.ts \

View file

@ -8,21 +8,23 @@ modules=""
modules="${modules} base" modules="${modules} base"
modules="${modules} call" modules="${modules} call"
modules="${modules} log" modules="${modules} log"
modules="${modules} conf"
modules="${modules} storage" modules="${modules} storage"
modules="${modules} database" modules="${modules} database"
modules="${modules} session" modules="${modules} session"
modules="${modules} file" modules="${modules} file"
modules="${modules} string" modules="${modules} string"
modules="${modules} structures"
modules="${modules} json" modules="${modules} json"
modules="${modules} ical" modules="${modules} ical"
modules="${modules} url" modules="${modules} url"
modules="${modules} http" modules="${modules} http"
modules="${modules} api" modules="${modules} api"
modules="${modules} rest" modules="${modules} rest"
modules="${modules} rest"
modules="${modules} server" modules="${modules} server"
modules="${modules} args" modules="${modules} args"
modules="${modules} auth" # modules="${modules} auth"
modules="${modules} bcrypt"
## exec ## exec