diff --git a/conf/example.json b/conf/example.json index d4d149c..6491c48 100644 --- a/conf/example.json +++ b/conf/example.json @@ -17,5 +17,11 @@ "kind": "internal", "data": { } + }, + "database": { + "kind": "sqlite", + "data": { + "path": "../zeitbild.sqlite" + } } } diff --git a/data/example.json b/data/example.json index ebf1d87..161a7ee 100644 --- a/data/example.json +++ b/data/example.json @@ -3,19 +3,22 @@ { "id": 1, "name": "alice", - "email_address": "alice@example.org", + "email_address": "alice@example.org", + "dav_token": null, "password": "alice" }, { "id": 2, "name": "bob", - "email_address": "bob@example.org", + "email_address": "bob@example.org", + "dav_token": "a5f10bc2d4ded8c5a07c5cb1c4e8b74363abce59d626574f0a83e67d499d9d5f", "password": "bob" }, { "id": 3, "name": "charlie", - "email_address": "charlie@example.org", + "email_address": "charlie@example.org", + "dav_token": null, "password": "charlie" } ], diff --git a/source/api/actions/davina_calendars.ts b/source/api/actions/davina_calendars.ts new file mode 100644 index 0000000..300f7f9 --- /dev/null +++ b/source/api/actions/davina_calendars.ts @@ -0,0 +1,75 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_davina_calendars( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + null, + null | Array< + { + id : int; + name : string; + access_level : string; + } + > + > + ( + rest_subject, + lib_plankton.http.enum_method.get, + "/davina/calendars", + { + "input_schema": () => ({ + "nullable": true, + }), + "output_schema": () => ({ + "nullable": false, + "type": "boolean", + }), + "query_parameters": () => ([ + { + "name": "username", + "required": true, + "description": "username", + }, + ]), + /** + * @todo + */ + "restriction": restriction_none, + "execution": async ({"query_parameters": query_parameters}) => { + const username : string = query_parameters["username"]; + const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(username); + + return ( + _zeitbild.service.calendar.overview(user_id) + .then( + (data_raw) => Promise.resolve( + data_raw + .map( + (entry) => ({ + "id": entry.id, + "name": entry.name, + "access_level": _zeitbild.value_object.access_level.to_string(entry.access_level), + }) + ) + ) + ) + .then( + (data) => Promise.resolve({ + "status_code": 200, + "data": data, + }) + ) + ); + }, + } + ); + } + +} + diff --git a/source/api/actions/davina_check.ts b/source/api/actions/davina_check.ts new file mode 100644 index 0000000..7de64b2 --- /dev/null +++ b/source/api/actions/davina_check.ts @@ -0,0 +1,57 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_davina_check( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + null, + boolean + > + ( + rest_subject, + lib_plankton.http.enum_method.get, + "/davina/check", + { + "input_schema": () => ({ + "nullable": true, + }), + "output_schema": () => ({ + "nullable": false, + "type": "boolean", + }), + "query_parameters": () => ([ + { + "name": "username", + "required": true, + "description": "username", + }, + { + "name": "password", + "required": true, + "description": "password", + }, + ]), + "restriction": restriction_none, + "execution": async ({"query_parameters": query_parameters}) => { + const username : string = query_parameters["username"]; + const password : string = query_parameters["password"]; + /** + * @todo [important] rectify + */ + const valid : boolean = (username === password); + return Promise.resolve({ + "status_code": 200, + "data": valid, + }); + }, + } + ); + } + +} + diff --git a/source/api/actions/davina_event_get.ts b/source/api/actions/davina_event_get.ts new file mode 100644 index 0000000..95d774d --- /dev/null +++ b/source/api/actions/davina_event_get.ts @@ -0,0 +1,78 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_davina_event_get( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + null, + lib_plankton.ical.type_vcalendar + >( + rest_subject, + lib_plankton.http.enum_method.get, + "/davina/event_get", + { + "query_parameters": () => ([ + { + "name": "username", + "required": true, + "description": "user name", + }, + { + "name": "calendar_id", + "required": true, + "description": "calendar ID", + }, + { + "name": "event_id", + "required": true, + "description": "event ID", + }, + ]), + "output_schema": () => ({ + "nullable": false, + "type": "string", + }), + "response_body_mimetype": "text/calendar", + "response_body_encode": (output) => Buffer.from( + (typeof(output) === "string") + ? + output + : + ( + lib_plankton.ical.ics_encode(output) + /** + * @todo add event encoder function to plankton + */ + // .replace(new RegExp("[\\s\\S]*BEGIN:VEVENT([\\s\\S]*)END:VEVENT[\\s\\S]*", "m"), "BEGIN:VEVENT$1END:VEVENT") + ) + ), + /** + * @todo + */ + "restriction": restriction_none, + "execution": async (stuff) => { + const username : string = stuff.query_parameters["username"]; + const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(username); + + const calendar_id : int = parseInt(stuff.query_parameters["calendar_id"]); + const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.service.calendar.get(calendar_id, user_id); + const event_id : int = parseInt(stuff.query_parameters["event_id"]); + const event_object : _zeitbild.type_event_object = await _zeitbild.service.resource.event_get(calendar_object.resource_id, event_id); + + return Promise.resolve( + { + "status_code": 200, + "data": _zeitbild.helpers.ical_vcalendar_from_own_event_list([event_object]), + } + ); + } + } + ); + } + +} diff --git a/source/api/actions/davina_event_list.ts b/source/api/actions/davina_event_list.ts new file mode 100644 index 0000000..c37d727 --- /dev/null +++ b/source/api/actions/davina_event_list.ts @@ -0,0 +1,114 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_davina_event_list( + rest_subject : lib_plankton.rest_caldav.type_rest + ) : void + { + register< + null, + ( + Array< + { + event_id : (null | int); + event_name : string; + } + > + | + string + ) + >( + rest_subject, + lib_plankton.http.enum_method.get, + "/davina/event_list", + { + "query_parameters": () => ([ + { + "name": "username", + "required": true, + "description": "user name", + }, + { + "name": "calendar_id", + "required": true, + "description": "calendar ID", + }, + ]), + "output_schema": () => ({ + "type": "array", + "items": { + "nullable": false, + "type": "object", + "additionalProperties": false, + "properties": { + "event_id": { + "nullable": true, + "type": "number", + }, + "event_name": { + "nullable": false, + "type": "string", + }, + }, + "required": [ + "event_id", + "event_name", + ], + } + }), + /** + * @todo + */ + "restriction": restriction_none, + "execution": async (stuff) => { + const username : string = stuff.query_parameters["username"]; + const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(username); + + /** + * @todo + */ + const from : lib_plankton.pit.type_pit = 0; + const to : lib_plankton.pit.type_pit = 1800000000; + const calendar_id : int = parseInt(stuff.query_parameters["calendar_id"]); + + return ( + _zeitbild.service.calendar.gather_events( + [calendar_id], + from, + to, + user_id + ) + .then( + (data) => Promise.resolve( + { + "status_code": 200, + "data": ( + data + .map( + (entry) => ({ + "event_id": entry.event_id, + "event_name": entry.event_object.name, + }) + ) + ), + } + ) + ) + .catch( + (reason) => Promise.resolve( + { + "status_code": 403, + "data": String(reason), + } + ) + ) + ); + } + } + ); + } + +} diff --git a/source/api/base.ts b/source/api/base.ts index 5132c31..012a127 100644 --- a/source/api/base.ts +++ b/source/api/base.ts @@ -29,7 +29,13 @@ namespace _zeitbild.api * @todo outsource? */ export async function web_auth( - authorization_string : (null | string) + authorization_string : (null | string), + { + "via_dav_token": via_dav_token = false, + } : { + via_dav_token ?: boolean; + } = { + } ) : Promise<(null | _zeitbild.type_user_id)> { if (authorization_string === null) { @@ -68,9 +74,16 @@ namespace _zeitbild.api return Promise.resolve<(null | _zeitbild.type_user_id)>(null); } else { - const password_shall : string = lib_plankton.sha256.get( - username, - _zeitbild.conf.get()["misc"]["auth_salt"] + const user_object : _zeitbild.type_user_object = await _zeitbild.service.user.get(user_id); + const password_shall : string = ( + via_dav_token + ? + user_object.dav_token + : + lib_plankton.sha256.get( + username, + _zeitbild.conf.get()["misc"]["auth_salt"] + ) ); if (! (password_is === password_shall)) { /** @@ -96,6 +109,13 @@ namespace _zeitbild.api } + /** + */ + export const restriction_none : lib_plankton.rest_caldav.type_restriction = ( + (stuff) => Promise.resolve(true) + ); + + /** */ export const restriction_logged_in : lib_plankton.rest_caldav.type_restriction = ( @@ -112,11 +132,16 @@ namespace _zeitbild.api export const restriction_basic_auth : lib_plankton.rest_caldav.type_restriction = ( (stuff) => ( web_auth( - stuff.headers["Authorization"] - ?? - stuff.headers["authorization"] - ?? - null + ( + stuff.headers["Authorization"] + ?? + stuff.headers["authorization"] + ?? + null + ), + { + "via_dav_token": false, + } ) .then( (user_id) => Promise.resolve( @@ -129,8 +154,26 @@ namespace _zeitbild.api /** */ - export const restriction_none : lib_plankton.rest_caldav.type_restriction = ( - (stuff) => Promise.resolve(true) + export const restriction_dav_token : lib_plankton.rest_caldav.type_restriction = ( + (stuff) => ( + web_auth( + ( + stuff.headers["Authorization"] + ?? + stuff.headers["authorization"] + ?? + null + ), + { + "via_dav_token": true, + } + ) + .then( + (user_id) => Promise.resolve( + (user_id !== null) + ) + ) + ) ); diff --git a/source/api/functions.ts b/source/api/functions.ts index 9504555..db222e3 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -59,6 +59,13 @@ namespace _zeitbild.api _zeitbild.api.register_caldav_projects(rest_subject); _zeitbild.api.register_caldav_get(rest_subject); } + // davina + { + _zeitbild.api.register_davina_check(rest_subject); + _zeitbild.api.register_davina_calendars(rest_subject); + _zeitbild.api.register_davina_event_list(rest_subject); + _zeitbild.api.register_davina_event_get(rest_subject); + } // misc { _zeitbild.api.register_users(rest_subject); diff --git a/source/main.ts b/source/main.ts index 8fec50a..697a1e9 100644 --- a/source/main.ts +++ b/source/main.ts @@ -8,6 +8,7 @@ type type_data = { id : int; name : string; email_address : string; + dav_token : (null | string); password : string; } >; @@ -72,6 +73,7 @@ async function data_init( const user_object : _zeitbild.type_user_object = { "name": user_raw.name, "email_address": user_raw.email_address, + "dav_token": user_raw.dav_token, }; const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add( user_object diff --git a/source/repositories/user.ts b/source/repositories/user.ts index 1000cd3..1299250 100644 --- a/source/repositories/user.ts +++ b/source/repositories/user.ts @@ -53,6 +53,7 @@ namespace _zeitbild.repository.user return { "name": user_object.name, "email_address": user_object.email_address, + "dav_token": user_object.dav_token, }; } @@ -66,6 +67,7 @@ namespace _zeitbild.repository.user return { "name": row["name"], "email_address": row["email_address"], + "dav_token": row["dav_token"], }; } diff --git a/source/services/caldal.ts b/source/services/caldal.ts deleted file mode 100644 index c20fac7..0000000 --- a/source/services/caldal.ts +++ /dev/null @@ -1,5 +0,0 @@ - -namespace _zeitbild.service.caldav -{ - -} diff --git a/source/services/caldav.ts b/source/services/caldav.ts index f74d29a..441a2ff 100644 --- a/source/services/caldav.ts +++ b/source/services/caldav.ts @@ -251,6 +251,7 @@ namespace _zeitbild.service.caldav "propstats": ( false ? + // probably the wrong way [ { "prop": (props ?? []).map( @@ -284,6 +285,7 @@ namespace _zeitbild.service.caldav }, ] : + // probably the right way props.map( (prop) => { const prop_parts : Array = prop.toLowerCase().split(":"); @@ -352,6 +354,7 @@ namespace _zeitbild.service.caldav user_id : type_user_id ) : Promise { + const http_protocol : string = "HTTP/1.1"; const answers : Record< string, (stuff : any) => lib_plankton.webdav.type_data_prop_value @@ -476,13 +479,13 @@ namespace _zeitbild.service.caldav ? { "prop": group.map(member => member.prop), - "status": "HTTP/1.1 200 OK", + "status": (http_protocol + " 200 OK"), "description": null, } : { "prop": group.map(member => member.prop), - "status": "HTTP/1.1 404 Not Found", + "status": (http_protocol + " 404 Not Found"), "description": null, } ) diff --git a/source/types.ts b/source/types.ts index 5a269c9..d27d7c2 100644 --- a/source/types.ts +++ b/source/types.ts @@ -28,6 +28,11 @@ namespace _zeitbild | string ); + dav_token : ( + null + | + string + ); }; @@ -82,6 +87,9 @@ namespace _zeitbild } | { + /** + * @todo rename to "ics_feed" or sth. + */ kind : "caldav"; data : { url : string; diff --git a/tools/makefile b/tools/makefile index c7de353..d7e2a36 100644 --- a/tools/makefile +++ b/tools/makefile @@ -83,6 +83,10 @@ ${dir_temp}/zeitbild-unlinked.js: \ ${dir_source}/api/actions/caldav_probe_via_well_known.ts \ ${dir_source}/api/actions/caldav_projects.ts \ ${dir_source}/api/actions/caldav_get.ts \ + ${dir_source}/api/actions/davina_check.ts \ + ${dir_source}/api/actions/davina_calendars.ts \ + ${dir_source}/api/actions/davina_event_list.ts \ + ${dir_source}/api/actions/davina_event_get.ts \ ${dir_source}/api/functions.ts \ ${dir_source}/main.ts @ ${cmd_log} "compile …"