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

View file

@ -1,4 +1,4 @@
.Dd $Mdocdate: February 14 2023 $
.Dd $Mdocdate: March 6 2023 $
.Dt USER 3
.Os Telodendria Project
.Sh NAME
@ -26,14 +26,55 @@
.Fn UserGetName "User *"
.Ft int
.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
.Pp
The
.Nm
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,
and provides features for authenticating local users, among other tasks.
.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
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
@ -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
store passwords in plain text, so this function hashes the password and and
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
.Pp
.Fn UserValidate ,
.Fn UserHistoricalValidate ,
.Fn UserExists ,
.Fn UserUnlock ,
.Fn UserCheckPassword ,
.Fn UserSetPassword ,
.Fn UserDeactivate ,
.Fn UserAccessTokenSave ,
.Fn UserDeleteToken ,
and
.Fn UserCheckPassword
.Fn UserDeleteTokens
all return a boolean value. Non-zero values indicate success, and zero values
indicate failure.
.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.
.Pp
.Fn UserLogin
returns a UserLoginInfo struct, which is defined as follows:
.Bd -literal -offset indent
typedef struct UserLoginInfo
{
char *accessToken;
char *refreshToken;
char *deviceId;
long tokenLifetime;
} UserLoginInfo;
.Ed
.Pp
returns a UserLoginInfo struct, or
.Dv NULL
if something goes wrong.
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.
.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
.Xr Db 3

View file

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

View file

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

View file

@ -212,7 +212,9 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
char *authType;
Array *completed;
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;
DbRef *dbRef;
@ -353,7 +355,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
}
type = JsonValueAsString(HashMapGet(identifier, "type"));
userId = UserParseId(JsonValueAsString(HashMapGet(identifier, "user")),
userId = UserIdParse(JsonValueAsString(HashMapGet(identifier, "user")),
config->serverName);
if (!type || strcmp(type, "m.id.user") != 0
@ -361,7 +363,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
{
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
ret = BuildResponse(flows, db, response, session, dbRef);
UserFreeId(userId);
UserIdFree(userId);
goto finish;
}
@ -370,7 +372,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
{
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
ret = BuildResponse(flows, db, response, session, dbRef);
UserFreeId(userId);
UserIdFree(userId);
goto finish;
}
@ -378,12 +380,12 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
{
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
ret = BuildResponse(flows, db, response, session, dbRef);
UserFreeId(userId);
UserIdFree(userId);
UserUnlock(user);
goto finish;
}
UserFreeId(userId);
UserIdFree(userId);
UserUnlock(user);
}
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 */
result->accessToken = UserGenerateAccessToken(user, deviceId, withRefresh);
result->accessToken = UserAccessTokenGenerate(user, deviceId, withRefresh);
UserAccessTokenSave(user->db, result->accessToken);
if (withRefresh)
@ -444,7 +444,7 @@ UserGetDevices(User * user)
}
UserAccessToken *
UserGenerateAccessToken(User * user, char *deviceId, int withRefresh)
UserAccessTokenGenerate(User * user, char *deviceId, int withRefresh)
{
UserAccessToken *token;
@ -641,7 +641,7 @@ UserDeleteTokens(User * user)
}
UserId *
UserParseId(char *id, char *defaultServer)
UserIdParse(char *id, char *defaultServer)
{
UserId *userId;
@ -693,13 +693,19 @@ UserParseId(char *id, char *defaultServer)
userId->server = StrDuplicate(defaultServer);
}
if (!UserHistoricalValidate(userId->localpart, userId->server))
{
UserIdFree(userId);
userId = NULL;
}
finish:
Free(id);
return userId;
}
void
UserFreeId(UserId * id)
UserIdFree(UserId * id)
{
if (id)
{

View file

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