/* * Copyright (C) 2022-2023 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_USER_H #define TELODENDRIA_USER_H /*** * @Nm User * @Nd Convenience functions for working with local users. * @Dd April 28 2023 * @Xr Db * * The * .Nm * API provides a wrapper over the database and offers an easy way to * manage local users. It supports all of the locking mechanisms that * the database does, and provides features for authenticating local * users, among many other tasks. */ #include <Db.h> #include <Json.h> /** * Many functions here operate on an opaque user structure. */ typedef struct User User; /** * Local users can have privileges to access the administrator API. * These are the individual privileges that Telodendria supports. * Note that they are bit flags, so they can be bitwise OR-ed together * to have multiple privileges. */ typedef enum UserPrivileges { USER_NONE = 0, USER_DEACTIVATE = (1 << 0), USER_ISSUE_TOKENS = (1 << 1), USER_CONFIG = (1 << 2), USER_GRANT_PRIVILEGES = (1 << 3), USER_PROC_CONTROL = (1 << 4), USER_ALL = ((1 << 5) - 1) } UserPrivileges; /** * A description of an access token, which users use to authenticate * with the client-server API. */ typedef struct UserAccessToken { char *user; char *string; char *deviceId; long lifetime; } UserAccessToken; /** * Login information, which is in most cases returned to the user * upon a successful login. */ typedef struct UserLoginInfo { UserAccessToken *accessToken; char *refreshToken; } UserLoginInfo; /** * A description of a Matrix user ID. */ typedef struct UserId { char *localpart; char *server; } UserId; /** * Take a localpart and domain as separate parameters and validate them * against the rules of the Matrix specification. The reasion the * domain is required is because the spec imposes limitations on the * length of the user ID, so the longer the domain name is, the shorter * the local part is allowed to be. This function is used to ensure * that client-provided Matrix IDs are valid on this server. */ extern int UserValidate(char *, char *); /** * This function behaves just like * .Fn UserValidate , * except that it is a little more lenient in what is considers to be * a valid Matrix ID. This is typically to validate users that exist * on other servers, since some usernames may exist that are not fully * spec compliant but remain in use since before the new restrictions * were put in place. */ extern int UserHistoricalValidate(char *, char *); /** * Determine whether the user identified by the specified localpart * exists in the database. */ extern int UserExists(Db *, char *); /** * Create a new user with the specified localpart and password, in * that order. */ extern User * UserCreate(Db *, char *, char *); /** * Take a localpart and obtain a database reference to the user * identified by that localpart. This function behaves analogously * to * .Fn DbLock , * and in fact it uses it under the hood to ensure that the user can * only be modified by the thread that has locked it. */ extern User * UserLock(Db *, char *); /** * Take an access token, figure out what user it belongs to, and then * returns a reference to that user. This function should be used by * most endpoints that require user authentication, since most * endpoints are authenticated via access tokens. */ extern User * UserAuthenticate(Db *, char *); /** * Return a user reference back to the database. This function uses * .Fn DbUnlock * under the hood. */ extern int UserUnlock(User *); /** * Log in a user. This function takes the user's password, desired * device ID and display name, and a boolean value indicating whether * or not the client supports refresh tokens. This function logs the * the user in and generates an access token to be returned to the * client. */ extern UserLoginInfo * UserLogin(User *, char *, char *, char *, int); /** * Get the localpart attached to a user object. This function may be * useful in the few cases where the localpart is not known already. */ extern char * UserGetName(User *); /** * Get the device ID attached to a user object, or NULL if the user * reference was not obtained using * .Fn UserAuthenticate . * If * .Fn UserLogin * is used, the return value will have the device ID in it, but the * device ID is not set on the user reference. */ extern char * UserGetDeviceId(User *); /** * Take a password and verify it against a user object. Telodendria * does not store passwords in plain text, so this function hashes the * password and checks it against what is stored in the database. */ extern int UserCheckPassword(User *, char *); /** * Reset the given user's password by hashing a plain text password and * storing it in the database. */ extern int UserSetPassword(User *, char *); /** * Immediately deactivate the given user account such that it can no * longer be used to log in, but the username is still reserved. This * is to prevent future users from pretending to be a previous user * of a given localpart. The user is logged out; all access tokens are * invalidated. */ extern int UserDeactivate(User *); /** * Return a boolean value indicating whether or not the user was * deactivated using * .Fn UserDeactivate . * Note that once deactivated, users cannot be reactivated. */ extern int UserDeactivated(User *); /** * 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 . */ extern HashMap * UserGetDevices(User *); /** * Generate a new access token for the given user. This is mainly * used internally. It takes the device ID and a boolean value * indicating whether or not the token should expire. */ extern UserAccessToken * UserAccessTokenGenerate(User *, char *, int); /** * Write the specified access token to the database, returning a * boolean value indicating success. */ extern int UserAccessTokenSave(Db *, UserAccessToken *); /** * Free the memory associated with the given access token. */ extern void UserAccessTokenFree(UserAccessToken *); /** * Delete a specific access token by name. */ extern int UserDeleteToken(User *, char *); /** * Get a string property from the user's profile given the specified * key. */ extern char * UserGetProfile(User *, char *); /** * Set a string property on the user's profile. A key/value pair should * be provided. */ extern void UserSetProfile(User *, char *, char *); /** * Delete all of the access tokens that belong to the specified user, * except for the one provided by name, unless NULL is provided for * the name. */ extern int UserDeleteTokens(User *, char *); /** * Get the current privileges of the user as a packed bit field. Use * the flags defined in UserPrivileges to deterine what privileges a * user has. */ extern int UserGetPrivileges(User *); /** * Set the privileges of the user. */ extern int UserSetPrivileges(User *, int); /** * Decode the JSON that represents the user privileges into a packed * bit field for simple manipulation. */ extern int UserDecodePrivileges(JsonValue *); /** * Encode the packed bit field that represents user privileges as a * JSON value. */ extern JsonValue *UserEncodePrivileges(int); /** * Convert a string privilege into its bit in the bit field. This is * mainly intended to be used internally. At the time of writing, I * don't recall exactly why it's in the public API. */ extern int UserDecodePrivilege(const char *); /** * Parse either a localpart or a fully qualified Matrix ID. If the * first argument is a localpart, then the second argument is used as * the server name. */ extern UserId * UserIdParse(char *, char *); /** * Free the memory associated with the parsed Matrix ID. */ extern void UserIdFree(UserId *); #endif /* TELODENDRIA_USER_H */