forked from Telodendria/Telodendria
Build out the User API a bit more.
This commit is contained in:
parent
46fe667988
commit
4b336de171
4 changed files with 174 additions and 48 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
181
src/User.c
181
src/User.c
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in a new issue