Build out the User API a bit more.

This commit is contained in:
Jordan Bancino 2023-02-17 03:18:24 +00:00
parent 46fe667988
commit 4b336de171
4 changed files with 174 additions and 48 deletions

View file

@ -249,14 +249,14 @@ ROUTE_IMPL(RouteLogin, args)
response = HashMapCreate(); response = HashMapCreate();
HashMapSet(response, "access_token", HashMapSet(response, "access_token",
JsonValueString(loginInfo->accessToken)); JsonValueString(loginInfo->accessToken->string));
HashMapSet(response, "device_id", HashMapSet(response, "device_id",
JsonValueString(loginInfo->deviceId)); JsonValueString(loginInfo->accessToken->deviceId));
if (refreshToken) if (refreshToken)
{ {
HashMapSet(response, "expires_in_ms", HashMapSet(response, "expires_in_ms",
JsonValueInteger(loginInfo->tokenLifetime)); JsonValueInteger(loginInfo->accessToken->lifetime));
HashMapSet(response, "refresh_token", HashMapSet(response, "refresh_token",
JsonValueString(loginInfo->refreshToken)); JsonValueString(loginInfo->refreshToken));
} }
@ -275,6 +275,7 @@ ROUTE_IMPL(RouteLogin, args)
* Don't need to free members; they're attached to the JSON * Don't need to free members; they're attached to the JSON
* response, they will be freed after the response is sent. * response, they will be freed after the response is sent.
*/ */
Free(loginInfo->accessToken);
Free(loginInfo); Free(loginInfo);
UserUnlock(user); UserUnlock(user);

View file

@ -209,14 +209,14 @@ ROUTE_IMPL(RouteRegister, args)
initialDeviceDisplayName, refreshToken); initialDeviceDisplayName, refreshToken);
HashMapSet(response, "access_token", HashMapSet(response, "access_token",
JsonValueString(loginInfo->accessToken)); JsonValueString(loginInfo->accessToken->string));
HashMapSet(response, "device_id", HashMapSet(response, "device_id",
JsonValueString(loginInfo->deviceId)); JsonValueString(loginInfo->accessToken->deviceId));
if (refreshToken) if (refreshToken)
{ {
HashMapSet(response, "expires_in_ms", HashMapSet(response, "expires_in_ms",
JsonValueInteger(loginInfo->tokenLifetime)); JsonValueInteger(loginInfo->accessToken->lifetime));
HashMapSet(response, "refresh_token", HashMapSet(response, "refresh_token",
JsonValueString(loginInfo->refreshToken)); JsonValueString(loginInfo->refreshToken));
} }
@ -225,6 +225,7 @@ ROUTE_IMPL(RouteRegister, args)
* Don't need to free members; they're attached to the JSON response, * Don't need to free members; they're attached to the JSON response,
* they will be freed after the response is sent. * they will be freed after the response is sent.
*/ */
Free(loginInfo->accessToken);
Free(loginInfo); Free(loginInfo);
} }

View file

@ -188,9 +188,6 @@ UserCreate(Db * db, char *name, char *password)
User *user = NULL; User *user = NULL;
HashMap *json = NULL; HashMap *json = NULL;
char *hash = NULL;
char *salt = NULL;
char *tmpstr = NULL;
unsigned long ts = UtilServerTs(); unsigned long ts = UtilServerTs();
/* TODO: Put some sort of password policy(like for example at least /* TODO: Put some sort of password policy(like for example at least
@ -224,16 +221,9 @@ UserCreate(Db * db, char *name, char *password)
return NULL; return NULL;
} }
UserSetPassword(user, password);
json = DbJson(user->ref); json = DbJson(user->ref);
/* Generate stored password using a salt and SHA256 */
salt = StrRandom(16);
tmpstr = StrConcat(2, password, salt);
hash = Sha256(tmpstr);
Free(tmpstr);
HashMapSet(json, "salt", JsonValueString(salt));
HashMapSet(json, "password", JsonValueString(hash));
HashMapSet(json, "createdOn", JsonValueInteger(ts)); HashMapSet(json, "createdOn", JsonValueInteger(ts));
HashMapSet(json, "deactivated", JsonValueBoolean(0)); HashMapSet(json, "deactivated", JsonValueBoolean(0));
@ -244,7 +234,6 @@ UserLoginInfo *
UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName, UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName,
int withRefresh) int withRefresh)
{ {
DbRef *atRef;
DbRef *rtRef = NULL; DbRef *rtRef = NULL;
HashMap *devices; HashMap *devices;
@ -269,38 +258,30 @@ UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName,
} }
result->refreshToken = NULL; result->refreshToken = NULL;
result->tokenLifetime = 0;
if (!deviceId)
{
deviceId = StrRandom(10);
}
else
{
deviceId = StrDuplicate(deviceId);
}
/* Generate an access token */ /* Generate an access token */
result->accessToken = StrRandom(64); result->accessToken = UserGenerateAccessToken(user, deviceId, withRefresh);
atRef = DbCreate(user->db, 3, "tokens", "access", result->accessToken); UserAccessTokenSave(user->db, result->accessToken);
HashMapSet(DbJson(atRef), "user", JsonValueString(StrDuplicate(user->name)));
if (withRefresh) if (withRefresh)
{ {
unsigned long ts = UtilServerTs();
result->tokenLifetime = 1000 * 60 * 60 * 24 * 7; /* 1 Week */
result->refreshToken = StrRandom(64); result->refreshToken = StrRandom(64);
rtRef = DbCreate(user->db, 3, "tokens", "refresh", result->refreshToken); rtRef = DbCreate(user->db, 3, "tokens", "refresh", result->refreshToken);
HashMapSet(DbJson(rtRef), "refreshes", HashMapSet(DbJson(rtRef), "refreshes",
JsonValueString(StrDuplicate(result->accessToken))); JsonValueString(StrDuplicate(result->accessToken->string)));
HashMapSet(DbJson(atRef), "expires", JsonValueInteger(ts + result->tokenLifetime));
DbUnlock(user->db, rtRef); DbUnlock(user->db, rtRef);
} }
if (!deviceId)
{
result->deviceId = StrRandom(10);
}
else
{
result->deviceId = StrDuplicate(deviceId);
}
devices = JsonValueAsObject(HashMapGet(DbJson(user->ref), "devices")); devices = JsonValueAsObject(HashMapGet(DbJson(user->ref), "devices"));
if (!devices) if (!devices)
{ {
@ -308,12 +289,14 @@ UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName,
HashMapSet(DbJson(user->ref), "devices", JsonValueObject(devices)); HashMapSet(DbJson(user->ref), "devices", JsonValueObject(devices));
} }
device = JsonValueAsObject(HashMapGet(devices, result->deviceId)); device = JsonValueAsObject(HashMapGet(devices, deviceId));
if (device) if (device)
{ {
JsonValue *val; JsonValue *val;
Free(deviceId);
val = HashMapDelete(device, "accessToken"); val = HashMapDelete(device, "accessToken");
if (val) if (val)
{ {
@ -331,7 +314,7 @@ UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName,
else else
{ {
device = HashMapCreate(); device = HashMapCreate();
HashMapSet(devices, StrDuplicate(result->deviceId), JsonValueObject(device)); HashMapSet(devices, deviceId, JsonValueObject(device));
if (deviceDisplayName) if (deviceDisplayName)
{ {
@ -348,10 +331,7 @@ UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName,
} }
HashMapSet(device, "accessToken", HashMapSet(device, "accessToken",
JsonValueString(StrDuplicate(result->accessToken))); JsonValueString(StrDuplicate(result->accessToken->string)));
HashMapSet(DbJson(atRef), "device", JsonValueString(StrDuplicate(result->deviceId)));
DbUnlock(user->db, atRef);
return result; return result;
} }
@ -400,3 +380,126 @@ UserCheckPassword(User * user, char *password)
return result; return result;
} }
int
UserSetPassword(User *user, char *password)
{
HashMap *json;
char *hash = NULL;
char *salt = NULL;
char *tmpstr = NULL;
if (!user || !password)
{
return 0;
}
json = DbJson(user->ref);
salt = StrRandom(16);
tmpstr = StrConcat(2, password, salt);
hash = Sha256(tmpstr);
Free(tmpstr);
JsonValueFree(HashMapSet(json, "salt", JsonValueString(salt)));
JsonValueFree(HashMapSet(json, "password", JsonValueString(hash)));
return 1;
}
int
UserDeactivate(User *user)
{
HashMap *json;
if (!user)
{
return 0;
}
json = DbJson(user->ref);
JsonValueFree(HashMapSet(json, "deactivated", JsonValueBoolean(1)));
return 1;
}
HashMap *
UserGetDevices(User *user)
{
HashMap *json;
if (!user)
{
return NULL;
}
json = DbJson(user->ref);
return JsonValueAsObject(HashMapGet(json, "devices"));
}
UserAccessToken *
UserGenerateAccessToken(User *user, char *deviceId, int withRefresh)
{
UserAccessToken *token;
if (!user || !deviceId)
{
return NULL;
}
token = Malloc(sizeof(UserAccessToken));
if (!token)
{
return NULL;
}
token->user = StrDuplicate(user->name);
token->deviceId = StrDuplicate(deviceId);
token->string = StrRandom(64);
if (withRefresh)
{
token->lifetime = 1000 * 60 * 60 * 24 * 7; /* 1 Week */
}
else
{
token->lifetime = 0;
}
return token;
}
int
UserAccessTokenSave(Db *db, UserAccessToken *token)
{
DbRef *ref;
HashMap *json;
if (!token)
{
return 0;
}
ref = DbCreate(db, 3, "tokens", "access", token->string);
if (!ref)
{
return 0;
}
json = DbJson(ref);
HashMapSet(json, "user", JsonValueString(token->user));
HashMapSet(json, "device", JsonValueString(token->deviceId));
if (token->lifetime)
{
HashMapSet(json, "expires", JsonValueInteger(UtilServerTs() + token->lifetime));
}
return DbUnlock(db, ref);
}

View file

@ -28,12 +28,18 @@
typedef struct User User; typedef struct User User;
typedef struct UserAccessToken
{
char *user;
char *string;
char *deviceId;
long lifetime;
} UserAccessToken;
typedef struct UserLoginInfo typedef struct UserLoginInfo
{ {
char *accessToken; UserAccessToken *accessToken;
char *refreshToken; char *refreshToken;
char *deviceId;
long tokenLifetime;
} UserLoginInfo; } UserLoginInfo;
extern int extern int
@ -66,4 +72,19 @@ extern char *
extern int extern int
UserCheckPassword(User *, char *); UserCheckPassword(User *, char *);
extern int
UserSetPassword(User *, char *);
extern int
UserDeactivate(User *);
extern HashMap *
UserGetDevices(User *);
extern UserAccessToken *
UserGenerateAccessToken(User *, char *, int);
extern int
UserAccessTokenSave(Db *, UserAccessToken *);
#endif /* TELODENDRIA_USER_H */ #endif /* TELODENDRIA_USER_H */