diff --git a/src/Routes/RouteLogin.c b/src/Routes/RouteLogin.c index 9b242cb..996a390 100644 --- a/src/Routes/RouteLogin.c +++ b/src/Routes/RouteLogin.c @@ -271,12 +271,15 @@ ROUTE_IMPL(RouteLogin, args) MatrixClientWellKnown(args->matrixArgs->config->baseUrl, args->matrixArgs->config->identityServer))); + Free(loginInfo->accessToken->user); + /* - * Don't need to free members; they're attached to the JSON + * Don't need to free other members; they're attached to the JSON * response, they will be freed after the response is sent. */ Free(loginInfo->accessToken); Free(loginInfo); + UserUnlock(user); break; diff --git a/src/Routes/RouteLogout.c b/src/Routes/RouteLogout.c new file mode 100644 index 0000000..149df84 --- /dev/null +++ b/src/Routes/RouteLogout.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +#include + +#include +#include +#include +#include +#include + +ROUTE_IMPL(RouteLogout, args) +{ + HashMap *response = NULL; + + char *tokenstr; + + Db *db = args->matrixArgs->db; + + User *user; + + if (HttpRequestMethodGet(args->context) != HTTP_POST) + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + return MatrixErrorCreate(M_UNRECOGNIZED); + } + + if (MATRIX_PATH_PARTS(args->path) > 1) + { + HttpResponseStatus(args->context, HTTP_NOT_FOUND); + return MatrixErrorCreate(M_NOT_FOUND); + } + + response = MatrixGetAccessToken(args->context, &tokenstr); + if (response) + { + return response; + } + + user = UserAuthenticate(db, tokenstr); + if (!user) + { + HttpResponseStatus(args->context, HTTP_UNAUTHORIZED); + return MatrixErrorCreate(M_UNKNOWN_TOKEN); + } + + if (MATRIX_PATH_PARTS(args->path) == 1) + { + char *pathPart = MATRIX_PATH_POP(args->path); + + if (!MATRIX_PATH_EQUALS(pathPart, "all")) + { + Free(pathPart); + HttpResponseStatus(args->context, HTTP_NOT_FOUND); + response = MatrixErrorCreate(M_NOT_FOUND); + goto finish; + } + + Free(pathPart); + + /* TODO: Implement /all */ + HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); + response = MatrixErrorCreate(M_UNKNOWN); + } + else + { + if (!UserDeleteToken(user, tokenstr)) + { + HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); + response = MatrixErrorCreate(M_UNKNOWN); + goto finish; + } + + response = HashMapCreate(); + } + +finish: + UserUnlock(user); + return response; +} diff --git a/src/Routes/RouteMatrix.c b/src/Routes/RouteMatrix.c index 124799f..8872981 100644 --- a/src/Routes/RouteMatrix.c +++ b/src/Routes/RouteMatrix.c @@ -68,6 +68,10 @@ ROUTE_IMPL(RouteMatrix, args) { response = RouteLogin(args); } + else if (MATRIX_PATH_EQUALS(pathPart, "logout")) + { + response = RouteLogout(args); + } else if (MATRIX_PATH_EQUALS(pathPart, "register")) { response = RouteRegister(args); diff --git a/src/Routes/RouteRegister.c b/src/Routes/RouteRegister.c index 5570c99..faf95bf 100644 --- a/src/Routes/RouteRegister.c +++ b/src/Routes/RouteRegister.c @@ -111,7 +111,7 @@ ROUTE_IMPL(RouteRegister, args) /* TODO: Add registration token flow */ uiaResult = UiaComplete(uiaFlows, args->context, - args->matrixArgs->db, request, &response); + args->matrixArgs->db, request, &response); if (uiaResult < 0) { diff --git a/src/Uia.c b/src/Uia.c index 3be4bec..9617541 100644 --- a/src/Uia.c +++ b/src/Uia.c @@ -39,7 +39,7 @@ struct UiaStage }; static HashMap * -BuildFlows(Array *flows) +BuildFlows(Array * flows) { HashMap *response; Array *responseFlows; @@ -101,7 +101,7 @@ BuildFlows(Array *flows) } static int -BuildResponse(Array *flows, char *session, Db *db, HashMap **response) +BuildResponse(Array * flows, char *session, Db * db, HashMap ** response) { DbRef *ref; HashMap *json; @@ -162,6 +162,7 @@ BuildResponse(Array *flows, char *session, Db *db, HashMap **response) for (i = 0; i < ArraySize(dbCompleted); i++) { char *stage = JsonValueAsString(ArrayGet(dbCompleted, i)); + ArrayAdd(completed, JsonValueString(StrDuplicate(stage))); } @@ -178,6 +179,7 @@ Array * UiaDummyFlow(void) { Array *response = ArrayCreate(); + if (!response) { return NULL; @@ -189,7 +191,7 @@ UiaDummyFlow(void) } UiaStage * -UiaBuildStage(char *type, HashMap *params) +UiaBuildStage(char *type, HashMap * params) { UiaStage *stage = Malloc(sizeof(UiaStage)); @@ -205,8 +207,8 @@ UiaBuildStage(char *type, HashMap *params) } int -UiaComplete(Array *flows, HttpServerContext * context, Db * db, - HashMap * request, HashMap ** response) +UiaComplete(Array * flows, HttpServerContext * context, Db * db, + HashMap * request, HashMap ** response) { JsonValue *val; HashMap *auth; @@ -248,7 +250,7 @@ UiaComplete(Array *flows, HttpServerContext * context, Db * db, } auth = JsonValueAsObject(val); - val = HashMapGet(request, "session"); + val = HashMapGet(auth, "session"); if (!val || JsonValueType(val) != JSON_STRING) { @@ -289,10 +291,12 @@ finish: for (i = 0; i < ArraySize(flows); i++) { Array *stages = ArrayGet(flows, i); + for (j = 0; j < ArraySize(stages); j++) { UiaStage *stage = ArrayGet(stages, j); - Free(stage); /* Members are referenced elsewhere */ + + Free(stage); /* Members are referenced elsewhere */ } ArrayFree(stages); } diff --git a/src/User.c b/src/User.c index a9b23b3..28aed90 100644 --- a/src/User.c +++ b/src/User.c @@ -150,7 +150,7 @@ UserAuthenticate(Db * db, char *accessToken) return NULL; } - if (UtilServerTs() >= (unsigned long) expires) + if (expires && UtilServerTs() >= (unsigned long) expires) { UserUnlock(user); DbUnlock(db, atRef); @@ -493,8 +493,8 @@ UserAccessTokenSave(Db * db, UserAccessToken * token) json = DbJson(ref); - HashMapSet(json, "user", JsonValueString(token->user)); - HashMapSet(json, "device", JsonValueString(token->deviceId)); + HashMapSet(json, "user", JsonValueString(StrDuplicate(token->user))); + HashMapSet(json, "device", JsonValueString(StrDuplicate(token->deviceId))); if (token->lifetime) { @@ -503,3 +503,78 @@ UserAccessTokenSave(Db * db, UserAccessToken * token) return DbUnlock(db, ref); } + +int +UserDeleteToken(User * user, char *token) +{ + char *username = NULL; + char *deviceid = NULL; + + Db *db = NULL; + + DbRef *tokenref = NULL; + + HashMap *tokenjson = NULL; + HashMap *userjson = NULL; + HashMap *deviceobject = NULL; + + JsonValue *devicejson = NULL; + JsonValue *deletedval = NULL; + + if (!user || !token) + { + return 0; + } + + db = user->db; + /* First check if the token even exists */ + if (!DbExists(db, 3, "tokens", "access", token)) + { + return 0; + } + + /* If it does, get it's username. */ + tokenref = DbLock(db, 3, "tokens", "access", token); + + if (!tokenref) + { + return 0; + } + tokenjson = DbJson(tokenref); + username = JsonValueAsString(HashMapGet(tokenjson, "user")); + deviceid = JsonValueAsString(HashMapGet(tokenjson, "device")); + + if (strcmp(username, UserGetName(user)) != 0) + { + /* Token does not match user, do not delete it */ + DbUnlock(db, tokenref); + return 0; + } + + /* Now delete it from the user */ + userjson = DbJson(user->ref); + devicejson = HashMapGet(userjson, "devices"); + if (JsonValueType(devicejson) == JSON_OBJECT) + { + char *key; + + /* Delete our object */ + deviceobject = JsonValueAsObject(devicejson); + key = HashMapGetKey(deviceobject, deviceid); + deletedval = HashMapDelete(deviceobject, deviceid); + if (!deletedval) + { + return 0; + } + Free(key); + JsonValueFree(deletedval); + } + + /* ... and now the token */ + if (!DbUnlock(db, tokenref) || !DbDelete(db, 3, "tokens", "access", token)) + { + return 0; + } + + return 1; +} diff --git a/src/include/Routes.h b/src/include/Routes.h index eba3a00..0d4fc8e 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -62,6 +62,7 @@ ROUTE(RouteWellKnown); /* /.well-known */ ROUTE(RouteMatrix); /* /_matrix */ ROUTE(RouteLogin); /* /_matrix/client/(r0|v3)/login */ +ROUTE(RouteLogout); /* /_matrix/client/(r0|v3)/logout */ ROUTE(RouteRegister); /* /_matrix/client/(r0|v3)/register */ ROUTE(RouteRefresh); /* /_matrix/client/(r0|v3)/refresh */ diff --git a/src/include/Uia.h b/src/include/Uia.h index 2d69219..d672efc 100644 --- a/src/include/Uia.h +++ b/src/include/Uia.h @@ -32,15 +32,15 @@ typedef struct UiaStage UiaStage; extern UiaStage * -UiaBuildStage(char *, HashMap *); + UiaBuildStage(char *, HashMap *); extern Array * -UiaDummyFlow(void); + UiaDummyFlow(void); extern void UiaCleanup(MatrixHttpHandlerArgs *); extern int -UiaComplete(Array *stages, HttpServerContext *, Db *, HashMap *, HashMap **); + UiaComplete(Array * stages, HttpServerContext *, Db *, HashMap *, HashMap **); #endif diff --git a/src/include/User.h b/src/include/User.h index 957be4f..b8bbf85 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -87,4 +87,7 @@ extern UserAccessToken * extern int UserAccessTokenSave(Db *, UserAccessToken *); +extern int + UserDeleteToken(User *, char *); + #endif /* TELODENDRIA_USER_H */