Document User API

This commit is contained in:
Jordan Bancino 2023-03-06 22:09:57 +00:00
parent e73cea64b7
commit 0ac21d430a
7 changed files with 138 additions and 33 deletions

View file

@ -11,7 +11,7 @@ Key:
Milestone: v0.2.0 Milestone: v0.2.0
----------------- -----------------
[ ] Abstract /email/requestToken and /msidsn/requestToken [!] Abstract /email/requestToken and /msidsn/requestToken
- Call it RouteRequestToken - Call it RouteRequestToken
- Reference it all the places it's needed - Reference it all the places it's needed
- Finish implementing JSON parsing - Finish implementing JSON parsing
@ -22,8 +22,8 @@ Milestone: v0.2.0
[x] User login [x] User login
[x] Logout all [x] Logout all
[ ] Documentation [~] Documentation
[ ] User functions [x] User functions
[ ] Json functions [ ] Json functions
[ ] Db functions [ ] Db functions
[ ] Uia (move docs from Matrix) [ ] Uia (move docs from Matrix)
@ -64,6 +64,7 @@ Milestone: v0.3.0
registration is disabled. registration is disabled.
[ ] 4: Account management [ ] 4: Account management
[ ] Deactivate [ ] Deactivate
[ ] Make sure UserLogin() fails if user is deactivated.
[ ] Change password [ ] Change password
[ ] Whoami [ ] Whoami
[ ] 9: User Data [ ] 9: User Data

View file

@ -1,4 +1,4 @@
.Dd $Mdocdate: February 14 2023 $ .Dd $Mdocdate: March 6 2023 $
.Dt USER 3 .Dt USER 3
.Os Telodendria Project .Os Telodendria Project
.Sh NAME .Sh NAME
@ -26,14 +26,55 @@
.Fn UserGetName "User *" .Fn UserGetName "User *"
.Ft int .Ft int
.Fn UserCheckPassword "User *" "char *" .Fn UserCheckPassword "User *" "char *"
.Ft int
.Fn UserSetPassword "User *" "char *"
.Ft int
.Fn UserDeactivate "User *"
.Ft HashMap *
.Fn UserGetDevices "User *"
.Ft UserAccessToken *
.Fn UserGenerateAccessToken "User *" "char *" "int"
.Ft int
.Fn UserAccessTokenSave "Db *" "UserAccessToken *"
.Ft void
.Fn UserAccessTokenFree "UserAccessToken *"
.Ft int
.Fn UserDeleteToken "User *" "char *"
.Ft int
.Fn UserDeleteTokens "User *"
.Ft UserId *
.Fn UserIdParse "char *" "char *"
.Ft void
.Fn UserIdFree "UserId *"
.Sh DESCRIPTION .Sh DESCRIPTION
.Pp
The The
.Nm .Nm
API provides a wrapper over the database and offers an easy way for managing API provides a wrapper over the database and offers an easy way for managing
local users. It supports all of the locking mechanisms that the database does, local users. It supports all of the locking mechanisms that the database does,
and provides features for authenticating local users, among other tasks. and provides features for authenticating local users, among other tasks.
.Pp .Pp
.Bd -literal -offset indent
typedef struct UserLoginInfo
{
UserAccessToken *accessToken;
char *refreshToken;
} UserLoginInfo;
typedef struct UserAccessToken
{
char *user;
char *string;
char *deviceId;
long lifetime;
} UserAccessToken;
typedef struct UserId
{
char *localpart;
char *server;
} UserId;
.Ed
.Pp
.Fn UserValidate .Fn UserValidate
takes a localpart and domain as separate parameters and validates it against the takes a localpart and domain as separate parameters and validates it against the
rules of the Matrix specification. The reason the domain is required is because rules of the Matrix specification. The reason the domain is required is because
@ -83,14 +124,58 @@ it's necessary to know the localpart of a user.
takes a password and verifies it against a user object. Telodendria does not takes a password and verifies it against a user object. Telodendria does not
store passwords in plain text, so this function hashes the password and and store passwords in plain text, so this function hashes the password and and
checks it against what's stored in the database. checks it against what's stored in the database.
.Pp
.Fn UserSetPassword
resets the given user's password by hashing a plain text password and
storing it in the database.
.Pp
.Fn UserDeactivate
deactivates a user such that it can no longer be used to log in, but
the username is still taken. This is to prevent future users from
pretending to be previous users of a given localpart.
.Pp
.Fn UserGetDevices
fetches the devices that belong to the user, in JSON format,
identical to what's stored in the database. In fact, this JSON is
still linked to the database, so it should not be freed with
.Fn JsonFree .
.Pp
.Fn UserAccessTokenGenerate ,
.Fn UserAccessTokenSave ,
and
.Fn UserAccessTokenFree
are used for managing individual access tokens on a user. They
operate on the UserAccessToken structure.
.Fn UserAccessTokenGenerate
takes the user localpart to generate the token for, the device ID,
for the token, and a boolean value indicating whether or not the token
should expire.
.Fn UserAccessTokenSave
writes the access token to the database.
.Pp
.Fn UserDeleteToken
and
.Fn UserDeleteTokens
delete a specific access token/refresh token pair, or all the access
and refresh tokens for a given user, respectively.
.Pp
.Fn UserIdParse
parses either a localpart or a fully-qualified Matrix ID.
.Fn UserIdFree
frees the result of this parsing.
.Sh RETURN VALUES .Sh RETURN VALUES
.Pp .Pp
.Fn UserValidate , .Fn UserValidate ,
.Fn UserHistoricalValidate , .Fn UserHistoricalValidate ,
.Fn UserExists , .Fn UserExists ,
.Fn UserUnlock , .Fn UserUnlock ,
.Fn UserCheckPassword ,
.Fn UserSetPassword ,
.Fn UserDeactivate ,
.Fn UserAccessTokenSave ,
.Fn UserDeleteToken ,
and and
.Fn UserCheckPassword .Fn UserDeleteTokens
all return a boolean value. Non-zero values indicate success, and zero values all return a boolean value. Non-zero values indicate success, and zero values
indicate failure. indicate failure.
.Pp .Pp
@ -106,18 +191,29 @@ by the given user pointer. This pointer should not be freed by the caller , as i
is used internally and will be freed when the user is unlocked. is used internally and will be freed when the user is unlocked.
.Pp .Pp
.Fn UserLogin .Fn UserLogin
returns a UserLoginInfo struct, which is defined as follows: returns a UserLoginInfo struct, or
.Bd -literal -offset indent .Dv NULL
typedef struct UserLoginInfo if something goes wrong.
{
char *accessToken;
char *refreshToken;
char *deviceId;
long tokenLifetime;
} UserLoginInfo;
.Ed
.Pp
All this information should be returned to the client that is logging in. If the All this information should be returned to the client that is logging in. If the
client doesn't support refresh tokens, then refreshToken will be NULL. client doesn't support refresh tokens, then refreshToken will be NULL.
.Pp
.Fn UserGetDevices
returns a JSON object that is linked to the database, or NULL if
there was an error. The result should not be freed with
.Fn JsonFree
because it is still directly attached to the database. This object
is an exact representation of what is stored on the disk.
.Pp
.Fn UserAccessTokenGenerate
generates an access token structure that should be freed when it is
no longer needed, or
.Dv NULL
if there was a memory error.
.Pp
.Fn UserIdParse
returns a UserId structure that should be freed when it is no longer
needed, or
.Dv NULL
if there was a memory error.
.Sh SEE ALSO .Sh SEE ALSO
.Xr Db 3 .Xr Db 3

View file

@ -160,7 +160,7 @@ ROUTE_IMPL(RouteLogin, args)
break; break;
} }
userId = UserParseId(JsonValueAsString(val), userId = UserIdParse(JsonValueAsString(val),
args->matrixArgs->config->serverName); args->matrixArgs->config->serverName);
if (!userId) if (!userId)
{ {
@ -292,7 +292,7 @@ ROUTE_IMPL(RouteLogin, args)
break; break;
} }
UserFreeId(userId); UserIdFree(userId);
JsonFree(request); JsonFree(request);
return response; return response;
} }

View file

@ -134,7 +134,7 @@ ROUTE_IMPL(RouteRefresh, args)
/* Generate a new access token associated with the device and user. */ /* Generate a new access token associated with the device and user. */
deviceId = JsonValueAsString(HashMapGet(DbJson(oAtRef), "device")); deviceId = JsonValueAsString(HashMapGet(DbJson(oAtRef), "device"));
newAccessToken = UserGenerateAccessToken(user, deviceId, 1); newAccessToken = UserAccessTokenGenerate(user, deviceId, 1);
UserAccessTokenSave(db, newAccessToken); UserAccessTokenSave(db, newAccessToken);
/* Replace old access token in User */ /* Replace old access token in User */

View file

@ -212,7 +212,9 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
char *authType; char *authType;
Array *completed; Array *completed;
Array *possibleNext; Array *possibleNext;
int remaining[16]; /* There should never be more than this many stages in a flow, right? */ int remaining[16]; /* There should never be more than
* this many stages in a flow,
* right? */
size_t i; size_t i;
DbRef *dbRef; DbRef *dbRef;
@ -353,7 +355,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
} }
type = JsonValueAsString(HashMapGet(identifier, "type")); type = JsonValueAsString(HashMapGet(identifier, "type"));
userId = UserParseId(JsonValueAsString(HashMapGet(identifier, "user")), userId = UserIdParse(JsonValueAsString(HashMapGet(identifier, "user")),
config->serverName); config->serverName);
if (!type || strcmp(type, "m.id.user") != 0 if (!type || strcmp(type, "m.id.user") != 0
@ -361,7 +363,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
{ {
HttpResponseStatus(context, HTTP_UNAUTHORIZED); HttpResponseStatus(context, HTTP_UNAUTHORIZED);
ret = BuildResponse(flows, db, response, session, dbRef); ret = BuildResponse(flows, db, response, session, dbRef);
UserFreeId(userId); UserIdFree(userId);
goto finish; goto finish;
} }
@ -370,7 +372,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
{ {
HttpResponseStatus(context, HTTP_UNAUTHORIZED); HttpResponseStatus(context, HTTP_UNAUTHORIZED);
ret = BuildResponse(flows, db, response, session, dbRef); ret = BuildResponse(flows, db, response, session, dbRef);
UserFreeId(userId); UserIdFree(userId);
goto finish; goto finish;
} }
@ -378,12 +380,12 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
{ {
HttpResponseStatus(context, HTTP_UNAUTHORIZED); HttpResponseStatus(context, HTTP_UNAUTHORIZED);
ret = BuildResponse(flows, db, response, session, dbRef); ret = BuildResponse(flows, db, response, session, dbRef);
UserFreeId(userId); UserIdFree(userId);
UserUnlock(user); UserUnlock(user);
goto finish; goto finish;
} }
UserFreeId(userId); UserIdFree(userId);
UserUnlock(user); UserUnlock(user);
} }
else if (strcmp(authType, "m.login.registration_token") == 0) else if (strcmp(authType, "m.login.registration_token") == 0)

View file

@ -269,7 +269,7 @@ UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName,
} }
/* Generate an access token */ /* Generate an access token */
result->accessToken = UserGenerateAccessToken(user, deviceId, withRefresh); result->accessToken = UserAccessTokenGenerate(user, deviceId, withRefresh);
UserAccessTokenSave(user->db, result->accessToken); UserAccessTokenSave(user->db, result->accessToken);
if (withRefresh) if (withRefresh)
@ -444,7 +444,7 @@ UserGetDevices(User * user)
} }
UserAccessToken * UserAccessToken *
UserGenerateAccessToken(User * user, char *deviceId, int withRefresh) UserAccessTokenGenerate(User * user, char *deviceId, int withRefresh)
{ {
UserAccessToken *token; UserAccessToken *token;
@ -641,7 +641,7 @@ UserDeleteTokens(User * user)
} }
UserId * UserId *
UserParseId(char *id, char *defaultServer) UserIdParse(char *id, char *defaultServer)
{ {
UserId *userId; UserId *userId;
@ -693,13 +693,19 @@ UserParseId(char *id, char *defaultServer)
userId->server = StrDuplicate(defaultServer); userId->server = StrDuplicate(defaultServer);
} }
if (!UserHistoricalValidate(userId->localpart, userId->server))
{
UserIdFree(userId);
userId = NULL;
}
finish: finish:
Free(id); Free(id);
return userId; return userId;
} }
void void
UserFreeId(UserId * id) UserIdFree(UserId * id)
{ {
if (id) if (id)
{ {

View file

@ -88,7 +88,7 @@ extern HashMap *
UserGetDevices(User *); UserGetDevices(User *);
extern UserAccessToken * extern UserAccessToken *
UserGenerateAccessToken(User *, char *, int); UserAccessTokenGenerate(User *, char *, int);
extern int extern int
UserAccessTokenSave(Db *, UserAccessToken *); UserAccessTokenSave(Db *, UserAccessToken *);
@ -103,9 +103,9 @@ extern int
UserDeleteTokens(User *); UserDeleteTokens(User *);
extern UserId * extern UserId *
UserParseId(char *, char *); UserIdParse(char *, char *);
extern void extern void
UserFreeId(UserId *); UserIdFree(UserId *);
#endif /* TELODENDRIA_USER_H */ #endif /* TELODENDRIA_USER_H */