forked from lda/telodendria
Document User API
This commit is contained in:
parent
e73cea64b7
commit
0ac21d430a
7 changed files with 138 additions and 33 deletions
7
TODO.txt
7
TODO.txt
|
@ -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
|
||||
|
|
124
man/man3/User.3
124
man/man3/User.3
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
14
src/Uia.c
14
src/Uia.c
|
@ -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)
|
||||
|
|
14
src/User.c
14
src/User.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue