diff --git a/proposals/admin.7 b/proposals/admin.7 index 30c2c62..d2bddca 100644 --- a/proposals/admin.7 +++ b/proposals/admin.7 @@ -1,4 +1,4 @@ -.Dd $Mdocdate: March 4 2023 $ +.Dd $Mdocdate: April 16 2023 $ .Dt ADMIN 7 .Os Telodendria Project .Sh NAME @@ -49,6 +49,11 @@ endpoint. .It Dv ISSUE_TOKENS This allows users to create, modify and delete registration tokens. +.It Dv CONFIG +Users with this privilege can modify Telodendria's configuration. +.It Dv GRANT_PRIVILEGES +Users with this privilege can modify their own privileges or +the privileges of others. .It Dv ALL Users with this privilege can use .Em any diff --git a/src/RegToken.c b/src/RegToken.c index f132c46..9888ede 100644 --- a/src/RegToken.c +++ b/src/RegToken.c @@ -30,6 +30,7 @@ #include #include #include +#include int RegTokenValid(RegTokenInfo * token) @@ -139,6 +140,9 @@ RegTokenGetInfo(Db * db, char *token) ret->used = JsonValueAsInteger(HashMapGet(tokenJson, "used")); + ret->grants = + UserDecodePrivileges(HashMapGet(tokenJson, "grants")); + return ret; } @@ -192,7 +196,7 @@ RegTokenVerify(char *token) } RegTokenInfo * -RegTokenCreate(Db * db, char *name, char *owner, unsigned long expires, int uses) +RegTokenCreate(Db * db, char *name, char *owner, unsigned long expires, int uses, int privileges) { RegTokenInfo *ret; HashMap *tokenJson; @@ -233,6 +237,7 @@ RegTokenCreate(Db * db, char *name, char *owner, unsigned long expires, int uses ret->uses = uses; ret->created = timestamp; ret->expires = expires; + ret->grants = privileges; /* Write user info to database. */ tokenJson = DbJson(ret->ref); @@ -246,6 +251,7 @@ RegTokenCreate(Db * db, char *name, char *owner, unsigned long expires, int uses JsonValueInteger(ret->used)); HashMapSet(tokenJson, "uses", JsonValueInteger(ret->uses)); + HashMapSet(tokenJson, "grants", UserEncodePrivileges(privileges)); return ret; } diff --git a/src/Routes/RouteUserProfile.c b/src/Routes/RouteUserProfile.c index 7d47e2f..62de088 100644 --- a/src/Routes/RouteUserProfile.c +++ b/src/Routes/RouteUserProfile.c @@ -127,7 +127,7 @@ ROUTE_IMPL(RouteUserProfile, path, argp) strcmp(entry, "avatar_url") == 0) { /* Check if user has privilege to do that action. */ - if (!strcmp(userId->localpart, UserGetName(user))) + if (strcmp(userId->localpart, UserGetName(user)) == 0) { value = JsonValueAsString(HashMapGet(request, entry)); /* TODO: Make UserSetProfile notify other diff --git a/src/User.c b/src/User.c index 687fcf6..4ec4562 100644 --- a/src/User.c +++ b/src/User.c @@ -688,6 +688,140 @@ UserDeleteTokens(User * user, char *exempt) return 1; } +int +UserGetPrivileges(User *user) +{ + if (!user) + { + return USER_NONE; + } + + return UserDecodePrivileges(HashMapGet(DbJson(user->ref), "privileges")); +} + +int +UserSetPrivileges(User *user, int privileges) +{ + JsonValue *val; + + if (!user) + { + return 0; + } + + if (!privileges) + { + JsonValueFree(HashMapDelete(DbJson(user->ref), "privileges")); + return 1; + } + + val = UserEncodePrivileges(privileges); + if (!val) + { + return 0; + } + + JsonValueFree(HashMapSet(DbJson(user->ref), "privileges", val)); + return 1; +} + +int +UserDecodePrivileges(JsonValue *val) +{ + int privileges = USER_NONE; + + size_t i; + Array *arr; + + if (!val) + { + goto finish; + } + + if (JsonValueType(val) == JSON_ARRAY) + { + arr = JsonValueAsArray(val); + for (i = 0; i < ArraySize(arr); i++) + { + val = ArrayGet(arr, i); + if (!val || JsonValueType(val) != JSON_STRING) + { + continue; + } + + privileges |= UserDecodePrivilege(JsonValueAsString(val)); + } + } + +finish: + return privileges; +} + +int +UserDecodePrivilege(const char *p) +{ + if (!p) + { + return USER_NONE; + } + else if (strcmp(p, "ALL") == 0) + { + return USER_ALL; + } + else if (strcmp(p, "DEACTIVATE") == 0) + { + return USER_DEACTIVATE; + } + else if (strcmp(p, "ISSUE_TOKENS") == 0) + { + return USER_ISSUE_TOKENS; + } + else if (strcmp(p, "CONFIG") == 0) + { + return USER_CONFIG; + } + else if (strcmp(p, "GRANT_PRIVILEGES") == 0) + { + return USER_GRANT_PRIVILEGES; + } + else + { + return USER_NONE; + } +} + +JsonValue * +UserEncodePrivileges(int privileges) +{ + Array *arr = ArrayCreate(); + if (!arr) + { + return NULL; + } + + if (privileges & USER_ALL) + { + ArrayAdd(arr, JsonValueString("ALL")); + goto finish; + } + +#define A(priv, as) \ + if (privileges & priv) \ + { \ + ArrayAdd(arr, JsonValueString(as)); \ + } + + A(USER_DEACTIVATE, "DEACTIVATE"); + A(USER_ISSUE_TOKENS, "ISSUE_TOKENS"); + A(USER_CONFIG, "CONFIG"); + A(USER_GRANT_PRIVILEGES, "GRANT_PRIVILEGES"); + +#undef A + +finish: + return JsonValueArray(arr); +} + UserId * UserIdParse(char *id, char *defaultServer) { diff --git a/src/include/RegToken.h b/src/include/RegToken.h index 504e961..d9c08c8 100644 --- a/src/include/RegToken.h +++ b/src/include/RegToken.h @@ -40,6 +40,8 @@ typedef struct RegTokenInfo unsigned long created; unsigned long expires; + int grants; /* privileges */ + } RegTokenInfo; @@ -56,7 +58,7 @@ extern RegTokenInfo * RegTokenGetInfo(Db *, char *); extern RegTokenInfo * - RegTokenCreate(Db *, char *, char *, unsigned long, int); + RegTokenCreate(Db *, char *, char *, unsigned long, int, int); extern void RegTokenFree(RegTokenInfo *); diff --git a/src/include/User.h b/src/include/User.h index dd74c7f..b121b51 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -28,6 +28,14 @@ #include +#define USER_DEACTIVATE (1 << 0) +#define USER_ISSUE_TOKENS (1 << 1) +#define USER_CONFIG (1 << 2) +#define USER_GRANT_PRIVILEGES (1 << 3) + +#define USER_NONE 0 +#define USER_ALL ((1 << 4) - 1) + typedef struct User User; typedef struct UserAccessToken @@ -113,6 +121,16 @@ extern void extern int UserDeleteTokens(User *, char *); +extern int UserGetPrivileges(User *); + +extern int UserSetPrivileges(User *, int); + +extern int UserDecodePrivileges(JsonValue *); + +extern JsonValue *UserEncodePrivileges(int); + +extern int UserDecodePrivilege(const char *); + extern UserId * UserIdParse(char *, char *);