forked from Telodendria/Telodendria
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?
This commit is contained in:
parent
7703405c70
commit
6ce6cb4525
3 changed files with 153 additions and 25 deletions
36
src/Db.c
36
src/Db.c
|
@ -32,6 +32,9 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
struct Db
|
struct Db
|
||||||
{
|
{
|
||||||
char *dir;
|
char *dir;
|
||||||
|
@ -403,6 +406,38 @@ DbLockFromArr(Db * db, Array * args)
|
||||||
fp = fopen(file, "r+");
|
fp = fopen(file, "r+");
|
||||||
if (!fp)
|
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;
|
ref = NULL;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
@ -415,6 +450,7 @@ DbLockFromArr(Db * db, Array * args)
|
||||||
/* Lock the file on the disk */
|
/* Lock the file on the disk */
|
||||||
if (fcntl(fileno(fp), F_SETLK, &lock) < 0)
|
if (fcntl(fileno(fp), F_SETLK, &lock) < 0)
|
||||||
{
|
{
|
||||||
|
printf("fcntl() failed on %s (%s)\n", file, strerror(errno));
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
ref = NULL;
|
ref = NULL;
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
140
src/Uia.c
140
src/Uia.c
|
@ -101,9 +101,8 @@ BuildFlows(Array * flows)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
HashMap *json;
|
||||||
|
|
||||||
*response = BuildFlows(flows);
|
*response = BuildFlows(flows);
|
||||||
|
@ -135,8 +134,6 @@ BuildResponse(Array * flows, char *session, Db * db, HashMap ** response)
|
||||||
DbUnlock(db, ref);
|
DbUnlock(db, ref);
|
||||||
|
|
||||||
HashMapSet(*response, "completed", JsonValueArray(ArrayCreate()));
|
HashMapSet(*response, "completed", JsonValueArray(ArrayCreate()));
|
||||||
HashMapSet(*response, "session", JsonValueString(session));
|
|
||||||
Free(session);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -150,14 +147,6 @@ BuildResponse(Array * flows, char *session, Db * db, HashMap ** response)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref = DbLock(db, 2, "user_interactive", session);
|
|
||||||
if (!ref)
|
|
||||||
{
|
|
||||||
JsonFree(*response);
|
|
||||||
ArrayFree(completed);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
json = DbJson(ref);
|
json = DbJson(ref);
|
||||||
dbCompleted = JsonValueAsArray(HashMapGet(json, "completed"));
|
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, "completed", JsonValueArray(completed));
|
||||||
HashMapSet(*response, "session", JsonValueString(session));
|
|
||||||
|
|
||||||
DbUnlock(db, ref);
|
session = StrDuplicate(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashMapSet(*response, "session", JsonValueString(session));
|
||||||
|
Free(session);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,9 +207,13 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
HashMap *auth;
|
HashMap *auth;
|
||||||
char *session;
|
char *session;
|
||||||
char *authType;
|
char *authType;
|
||||||
|
Array *completed;
|
||||||
|
Array *possibleNext;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
DbRef *dbRef;
|
DbRef *dbRef;
|
||||||
HashMap *dbJson;
|
HashMap *dbJson;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!flows)
|
if (!flows)
|
||||||
{
|
{
|
||||||
|
@ -235,7 +230,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
if (!val)
|
if (!val)
|
||||||
{
|
{
|
||||||
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
||||||
return BuildResponse(flows, NULL, db, response);
|
return BuildResponse(flows, db, response, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JsonValueType(val) != JSON_OBJECT)
|
if (JsonValueType(val) != JSON_OBJECT)
|
||||||
|
@ -256,33 +251,130 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
}
|
}
|
||||||
|
|
||||||
session = JsonValueAsString(val);
|
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");
|
val = HashMapGet(auth, "type");
|
||||||
|
|
||||||
if (!val || JsonValueType(val) != JSON_STRING)
|
if (!val || JsonValueType(val) != JSON_STRING)
|
||||||
{
|
{
|
||||||
HttpResponseStatus(context, HTTP_BAD_REQUEST);
|
HttpResponseStatus(context, HTTP_BAD_REQUEST);
|
||||||
*response = MatrixErrorCreate(M_BAD_JSON);
|
*response = MatrixErrorCreate(M_BAD_JSON);
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
authType = JsonValueAsString(val);
|
authType = JsonValueAsString(val);
|
||||||
|
|
||||||
dbRef = DbLock(db, 2, "user_interactive", session);
|
for (i = 0; i < ArraySize(possibleNext); i++)
|
||||||
if (!dbRef)
|
|
||||||
{
|
{
|
||||||
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
char *possible = ArrayGet(possibleNext, i);
|
||||||
return BuildResponse(flows, StrDuplicate(session), db, response);
|
|
||||||
|
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);
|
DbUnlock(db, dbRef);
|
||||||
|
return ret;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UiaFlowsFree(Array *flows)
|
UiaFlowsFree(Array * flows)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,6 @@ extern int
|
||||||
UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **);
|
UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **);
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
UiaFlowsFree(Array *);
|
UiaFlowsFree(Array *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue