From 6ce6cb452530ce41c78ce85c4e7f66bc0552de12 Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Mon, 27 Feb 2023 15:39:12 +0000 Subject: [PATCH] Implement flow handling in Uia API. This commit should fix user interactive authentication for dummy flows, but I still have to implement a few more flows, including passwords and refresh token. I also have to fix the cleanup logic: when do we purge UIA sessions? --- src/Db.c | 36 ++++++++++++ src/Uia.c | 140 ++++++++++++++++++++++++++++++++++++++-------- src/include/Uia.h | 2 +- 3 files changed, 153 insertions(+), 25 deletions(-) diff --git a/src/Db.c b/src/Db.c index 8e0677d..0c2d35a 100644 --- a/src/Db.c +++ b/src/Db.c @@ -32,6 +32,9 @@ #include #include +#include +#include + struct Db { char *dir; @@ -403,6 +406,38 @@ DbLockFromArr(Db * db, Array * args) fp = fopen(file, "r+"); if (!fp) { + if (ref) + { + pthread_mutex_lock(&ref->lock); + + HashMapDelete(db->cache, hash); + JsonFree(ref->json); + StringArrayFree(ref->name); + + db->cacheSize -= ref->size; + + if (ref->next) + { + ref->next->prev = ref->prev; + } + else + { + db->mostRecent = ref->prev; + } + + if (ref->prev) + { + ref->prev->next = ref->next; + } + else + { + db->leastRecent = ref->next; + } + + pthread_mutex_unlock(&ref->lock); + pthread_mutex_destroy(&ref->lock); + Free(ref); + } ref = NULL; goto finish; } @@ -415,6 +450,7 @@ DbLockFromArr(Db * db, Array * args) /* Lock the file on the disk */ if (fcntl(fileno(fp), F_SETLK, &lock) < 0) { + printf("fcntl() failed on %s (%s)\n", file, strerror(errno)); fclose(fp); ref = NULL; goto finish; diff --git a/src/Uia.c b/src/Uia.c index 2ec3d89..4517a3c 100644 --- a/src/Uia.c +++ b/src/Uia.c @@ -101,9 +101,8 @@ BuildFlows(Array * flows) } static int -BuildResponse(Array * flows, char *session, Db * db, HashMap ** response) +BuildResponse(Array * flows, Db * db, HashMap ** response, char *session, DbRef * ref) { - DbRef *ref; HashMap *json; *response = BuildFlows(flows); @@ -135,8 +134,6 @@ BuildResponse(Array * flows, char *session, Db * db, HashMap ** response) DbUnlock(db, ref); HashMapSet(*response, "completed", JsonValueArray(ArrayCreate())); - HashMapSet(*response, "session", JsonValueString(session)); - Free(session); } else { @@ -150,14 +147,6 @@ BuildResponse(Array * flows, char *session, Db * db, HashMap ** response) return -1; } - ref = DbLock(db, 2, "user_interactive", session); - if (!ref) - { - JsonFree(*response); - ArrayFree(completed); - return -1; - } - json = DbJson(ref); dbCompleted = JsonValueAsArray(HashMapGet(json, "completed")); @@ -169,11 +158,13 @@ BuildResponse(Array * flows, char *session, Db * db, HashMap ** response) } HashMapSet(*response, "completed", JsonValueArray(completed)); - HashMapSet(*response, "session", JsonValueString(session)); - DbUnlock(db, ref); + session = StrDuplicate(session); } + HashMapSet(*response, "session", JsonValueString(session)); + Free(session); + return 0; } @@ -216,9 +207,13 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db, HashMap *auth; char *session; char *authType; + Array *completed; + Array *possibleNext; + size_t i; DbRef *dbRef; HashMap *dbJson; + int ret; if (!flows) { @@ -235,7 +230,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db, if (!val) { HttpResponseStatus(context, HTTP_UNAUTHORIZED); - return BuildResponse(flows, NULL, db, response); + return BuildResponse(flows, db, response, NULL, NULL); } if (JsonValueType(val) != JSON_OBJECT) @@ -256,33 +251,130 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db, } session = JsonValueAsString(val); + + dbRef = DbLock(db, 2, "user_interactive", session); + if (!dbRef) + { + HttpResponseStatus(context, HTTP_UNAUTHORIZED); + return BuildResponse(flows, db, response, NULL, NULL); + } + + dbJson = DbJson(dbRef); + + completed = JsonValueAsArray(HashMapGet(dbJson, "completed")); + possibleNext = ArrayCreate(); + + for (i = 0; i < ArraySize(flows); i++) + { + size_t j; + + Array *stages = ArrayGet(flows, i); + + if (ArraySize(stages) > ArraySize(completed)) + { + UiaStage *stage = ArrayGet(stages, ArraySize(completed)); + + ArrayAdd(possibleNext, stage->type); + } + else if (ArraySize(stages) == ArraySize(completed)) + { + for (j = 0; j < ArraySize(stages); j++) + { + UiaStage *stage = ArrayGet(stages, j); + char *flowStage = stage->type; + char *completedStage = JsonValueAsString(ArrayGet(completed, j)); + + if (strcmp(flowStage, completedStage) != 0) + { + break; + } + } + + if (j == ArraySize(stages)) + { + /* Success: completed matches a stage perfectly */ + ret = 1; + goto finish; + } + } + } + val = HashMapGet(auth, "type"); if (!val || JsonValueType(val) != JSON_STRING) { HttpResponseStatus(context, HTTP_BAD_REQUEST); *response = MatrixErrorCreate(M_BAD_JSON); - return 0; + ret = 0; + goto finish; } authType = JsonValueAsString(val); - dbRef = DbLock(db, 2, "user_interactive", session); - if (!dbRef) + for (i = 0; i < ArraySize(possibleNext); i++) { - HttpResponseStatus(context, HTTP_UNAUTHORIZED); - return BuildResponse(flows, StrDuplicate(session), db, response); + char *possible = ArrayGet(possibleNext, i); + + if (strcmp(authType, possible) == 0) + { + break; + } } - dbJson = DbJson(dbRef); + if (i == ArraySize(possibleNext)) + { + HttpResponseStatus(context, HTTP_UNAUTHORIZED); + ret = BuildResponse(flows, db, response, session, dbRef); + goto finish; + } + if (strcmp(authType, "m.login.dummy") == 0) + { + /* Do nothing */ + } + else if (strcmp(authType, "m.login.password") == 0) + { + /* TODO */ + } + else if (strcmp(authType, "m.login.registration_token") == 0) + { + /* TODO */ + } + else if (strcmp(authType, "m.login.recaptcha") == 0) + { + /* TODO */ + } + else if (strcmp(authType, "m.login.sso") == 0) + { + /* TODO */ + } + else if (strcmp(authType, "m.login.email.identity") == 0) + { + /* TODO */ + } + else if (strcmp(authType, "m.login.msisdn") == 0) + { + /* TODO */ + } + else + { + HttpResponseStatus(context, HTTP_UNAUTHORIZED); + ret = BuildResponse(flows, db, response, session, dbRef); + goto finish; + } + + ArrayAdd(completed, JsonValueString(authType)); + + ret = 1; + +finish: + ArrayFree(possibleNext); DbUnlock(db, dbRef); - - return 1; + return ret; } void -UiaFlowsFree(Array *flows) +UiaFlowsFree(Array * flows) { size_t i, j; diff --git a/src/include/Uia.h b/src/include/Uia.h index 2ef575b..0ee4fa3 100644 --- a/src/include/Uia.h +++ b/src/include/Uia.h @@ -44,6 +44,6 @@ extern int UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **); extern void -UiaFlowsFree(Array *); + UiaFlowsFree(Array *); #endif