Break out UserInteractiveAuth into its own header.

We'll need this because user interactive auth will get complicated and
messy very soon.
This commit is contained in:
Jordan Bancino 2022-12-26 15:48:21 +00:00
parent dbecb28395
commit c9e42ff813
7 changed files with 203 additions and 118 deletions

View file

@ -14,6 +14,7 @@ Milestone: v0.2.0
[x] Abstract user-interactive authentication [x] Abstract user-interactive authentication
[ ] Abstract /email/requestToken and /msidsn/requestToken [ ] Abstract /email/requestToken and /msidsn/requestToken
[ ] Document UserInteractiveAuth (move docs from Matrix)
[~] User registration [~] User registration
[x] Username validation [x] Username validation
[x] Password hashing [x] Password hashing

View file

@ -301,111 +301,6 @@ MatrixErrorCreate(MatrixError errorArg)
return errorObj; return errorObj;
} }
static HashMap *
BuildDummyFlow(void)
{
HashMap *response = HashMapCreate();
HashMap *dummyFlow = HashMapCreate();
Array *stages = ArrayCreate();
Array *flows = ArrayCreate();
ArrayAdd(stages,
JsonValueString(UtilStringDuplicate("m.login.dummy")));
HashMapSet(dummyFlow, "stages", JsonValueArray(stages));
ArrayAdd(flows, JsonValueObject(dummyFlow));
HashMapSet(response, "flows", JsonValueArray(flows));
HashMapSet(response, "params",
JsonValueObject(HashMapCreate()));
return response;
}
HashMap *
MatrixUserInteractiveAuth(HttpServerContext * context, Db * db,
HashMap * request)
{
JsonValue *auth;
JsonValue *type;
JsonValue *session;
HashMap *authObj;
char *typeStr;
char *sessionStr;
DbRef *ref;
auth = HashMapGet(request, "auth");
if (!auth)
{
HashMap *response = NULL;
HashMap *persist;
char *session = UtilRandomString(24);
ref = DbCreate(db, 2, "user_interactive", session);
persist = DbJson(ref);
HashMapSet(persist, "created",
JsonValueInteger(UtilServerTs()));
HashMapSet(persist, "completed", JsonValueBoolean(0));
DbUnlock(db, ref);
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
response = BuildDummyFlow();
HashMapSet(response, "session", JsonValueString(session));
return response;
}
if (JsonValueType(auth) != JSON_OBJECT)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_BAD_JSON);
}
authObj = JsonValueAsObject(auth);
type = HashMapGet(authObj, "type");
session = HashMapGet(authObj, "session");
if (!type || JsonValueType(type) != JSON_STRING)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_BAD_JSON);
}
if (!session || JsonValueType(session) != JSON_STRING)
{
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
return BuildDummyFlow();
}
typeStr = JsonValueAsString(session);
sessionStr = JsonValueAsString(session);
if (strcmp(typeStr, "m.login.dummy") != 0)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_INVALID_PARAM);
}
/* Check to see if session exists */
ref = DbLock(db, 2, "user_interactive", sessionStr);
if (!ref)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_UNKNOWN);
}
/* We only need to know that it exists. */
DbUnlock(db, ref);
DbDelete(db, 2, "user_interactive", sessionStr);
return NULL; /* All good, auth successful */
}
HashMap * HashMap *
MatrixAuthenticate(HttpServerContext * context, Db * db) MatrixAuthenticate(HttpServerContext * context, Db * db)
{ {

View file

@ -29,6 +29,7 @@
#include <HashMap.h> #include <HashMap.h>
#include <Util.h> #include <Util.h>
#include <Memory.h> #include <Memory.h>
#include <UserInteractiveAuth.h>
ROUTE_IMPL(RouteRegister, args) ROUTE_IMPL(RouteRegister, args)
{ {
@ -79,7 +80,7 @@ ROUTE_IMPL(RouteRegister, args)
response = MatrixErrorCreate(M_BAD_JSON); response = MatrixErrorCreate(M_BAD_JSON);
goto finish; goto finish;
} }
username = JsonValueAsString(val); username = UtilStringDuplicate(JsonValueAsString(val));
if (!MatrixUserValidate(username, args->matrixArgs->config->serverName)) if (!MatrixUserValidate(username, args->matrixArgs->config->serverName))
{ {
@ -87,9 +88,12 @@ ROUTE_IMPL(RouteRegister, args)
response = MatrixErrorCreate(M_INVALID_USERNAME); response = MatrixErrorCreate(M_INVALID_USERNAME);
goto finish; goto finish;
} }
/* TODO: Check if username exists and throw error if it
* does */
} }
response = MatrixUserInteractiveAuth(args->context, response = UserInteractiveAuth(args->context,
args->matrixArgs->db, request); args->matrixArgs->db, request);
if (response) if (response)
@ -123,7 +127,7 @@ ROUTE_IMPL(RouteRegister, args)
goto finish; goto finish;
} }
password = JsonValueAsString(val); password = UtilStringDuplicate(JsonValueAsString(val));
val = HashMapGet(request, "device_id"); val = HashMapGet(request, "device_id");
if (val) if (val)
@ -135,7 +139,7 @@ ROUTE_IMPL(RouteRegister, args)
goto finish; goto finish;
} }
deviceId = JsonValueAsString(val); deviceId = UtilStringDuplicate(JsonValueAsString(val));
} }
val = HashMapGet(request, "inhibit_login"); val = HashMapGet(request, "inhibit_login");
@ -161,7 +165,7 @@ ROUTE_IMPL(RouteRegister, args)
goto finish; goto finish;
} }
initialDeviceDisplayName = JsonValueAsString(val); initialDeviceDisplayName = UtilStringDuplicate(JsonValueAsString(val));
} }
val = HashMapGet(request, "refresh_token"); val = HashMapGet(request, "refresh_token");
@ -177,17 +181,27 @@ ROUTE_IMPL(RouteRegister, args)
refreshToken = JsonValueAsBoolean(val); refreshToken = JsonValueAsBoolean(val);
} }
/* TODO: Register new user here */ if (!username)
{
username = UtilRandomString(16);
}
if (!inhibitLogin && !deviceId)
{
deviceId = UtilRandomString(10);
}
/* These values are already set */ /* These values are already set */
(void) password; (void) password;
(void) refreshToken; (void) refreshToken;
(void) inhibitLogin; (void) inhibitLogin;
(void) username;
/* These may be NULL */ /* These may be NULL */
(void) username;
(void) deviceId;
(void) initialDeviceDisplayName; (void) initialDeviceDisplayName;
(void) deviceId;
/* TODO: Register new user here */
finish: finish:
JsonFree(request); JsonFree(request);

141
src/UserInteractiveAuth.c Normal file
View file

@ -0,0 +1,141 @@
/*
* Copyright (C) 2022 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 <UserInteractiveAuth.h>
#include <Json.h>
#include <Util.h>
#include <Matrix.h>
#include <string.h>
static HashMap *
BuildDummyFlow(void)
{
HashMap *response = HashMapCreate();
HashMap *dummyFlow = HashMapCreate();
Array *stages = ArrayCreate();
Array *flows = ArrayCreate();
ArrayAdd(stages,
JsonValueString(UtilStringDuplicate("m.login.dummy")));
HashMapSet(dummyFlow, "stages", JsonValueArray(stages));
ArrayAdd(flows, JsonValueObject(dummyFlow));
HashMapSet(response, "flows", JsonValueArray(flows));
HashMapSet(response, "params",
JsonValueObject(HashMapCreate()));
return response;
}
HashMap *
UserInteractiveAuth(HttpServerContext * context, Db * db,
HashMap * request)
{
JsonValue *auth;
JsonValue *type;
JsonValue *session;
HashMap *authObj;
char *typeStr;
char *sessionStr;
DbRef *ref;
auth = HashMapGet(request, "auth");
if (!auth)
{
HashMap *response = NULL;
HashMap *persist;
char *session = UtilRandomString(24);
ref = DbCreate(db, 2, "user_interactive", session);
persist = DbJson(ref);
HashMapSet(persist, "created",
JsonValueInteger(UtilServerTs()));
HashMapSet(persist, "completed", JsonValueBoolean(0));
DbUnlock(db, ref);
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
response = BuildDummyFlow();
HashMapSet(response, "session", JsonValueString(session));
return response;
}
if (JsonValueType(auth) != JSON_OBJECT)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_BAD_JSON);
}
authObj = JsonValueAsObject(auth);
type = HashMapGet(authObj, "type");
session = HashMapGet(authObj, "session");
if (!type || JsonValueType(type) != JSON_STRING)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_BAD_JSON);
}
if (!session || JsonValueType(session) != JSON_STRING)
{
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
return BuildDummyFlow();
}
typeStr = JsonValueAsString(session);
sessionStr = JsonValueAsString(session);
if (strcmp(typeStr, "m.login.dummy") != 0)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_INVALID_PARAM);
}
/* Check to see if session exists */
ref = DbLock(db, 2, "user_interactive", sessionStr);
if (!ref)
{
HttpResponseStatus(context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_UNKNOWN);
}
/* We only need to know that it exists. */
DbUnlock(db, ref);
DbDelete(db, 2, "user_interactive", sessionStr);
return NULL; /* All good, auth successful */
}
void
UserInteractiveAuthCleanup(Db * db)
{
}

View file

@ -80,9 +80,6 @@ extern void
extern HashMap * extern HashMap *
MatrixErrorCreate(MatrixError); MatrixErrorCreate(MatrixError);
extern HashMap *
MatrixUserInteractiveAuth(HttpServerContext *, Db *, HashMap *);
extern HashMap * extern HashMap *
MatrixAuthenticate(HttpServerContext *, Db *); MatrixAuthenticate(HttpServerContext *, Db *);

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) 2022 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.
*/
#ifndef TELODENDRIA_USERINTERACTIVEAUTH_H
#define TELODENDRIA_USERINTERACTIVEAUTH_H
#include <HashMap.h>
#include <HttpServer.h>
#include <Db.h>
extern void
UserInteractiveAuthCleanup(Db *);
extern HashMap *
UserInteractiveAuth(HttpServerContext *, Db *, HashMap *);
#endif