diff --git a/Schema/AdminToken.json b/Schema/AdminToken.json deleted file mode 100644 index 7c6ba7d..0000000 --- a/Schema/AdminToken.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "header": "Schema\/AdminToken.h", - "types": { - "TokenRequest": { - "fields": { - "name": { "type": "string" }, - "max_uses": { "type": "integer" }, - "lifetime": { "type": "integer" } - }, - "type": "struct" - }, - "TokenInfo": { - "fields": { - "name": { "type": "string" }, - "created_by": { "type": "string" }, - "created_on": { "type": "integer" }, - "expires_on": { "type": "integer" }, - "used": { "type": "integer" }, - "uses": { "type": "integer" } - }, - "type": "struct" - } - }, - "guard": "TELODENDRIA_ADMINTOKEN_H" -} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b28df2e..69f1e5b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,13 +29,16 @@ Matrix clients. (#35) ### New Features -- Implemented `/_telodendria/admin/v1/deactivate/[localpart]` for admins to be able to -deactivate users. - Moved all administrator API endpoints to `/_telodendria/admin/v1`, because later revisions of the administrator API may break clients, so we want a way to give those breaking revisions new endpoints. -- Implemented [a few](https://git.telodendria.io/Telodendria/Telodendria/issues/26) endpoints -for admins to manage tokens remotely +- Implemented `/_telodendria/admin/v1/deactivate/[localpart]` for admins to be able to +deactivate users. +- Implemented the following APIs for managing registration tokens: + - **GET** `/_telodendria/admin/tokens` + - **GET** `/_telodendria/admin/tokens/[token]` + - **POST** `/_telodendria/admin/tokens` + - **DELETE** `/_telodendria/admin/tokens/[token]` ## v0.3.0 diff --git a/src/RegToken.c b/src/RegToken.c index 3f8f66d..d842bab 100644 --- a/src/RegToken.c +++ b/src/RegToken.c @@ -30,10 +30,10 @@ #include #include #include -#include #include +#include -#include +#include int RegTokenValid(RegTokenInfo * token) @@ -101,8 +101,7 @@ RegTokenDelete(RegTokenInfo * token) { return 0; } - Free(token->name); - Free(token->owner); + RegTokenInfoFree(token); Free(token); return 1; } @@ -115,6 +114,8 @@ RegTokenGetInfo(Db * db, char *token) DbRef *tokenRef; HashMap *tokenJson; + char *errp = NULL; + if (!RegTokenExists(db, token)) { return NULL; @@ -128,36 +129,25 @@ RegTokenGetInfo(Db * db, char *token) tokenJson = DbJson(tokenRef); ret = Malloc(sizeof(RegTokenInfo)); + if (!RegTokenInfoFromJson(tokenJson, ret, &errp)) + { + Log(LOG_ERR, "RegTokenGetInfo(): Database decoding error: %s", errp); + RegTokenFree(ret); + return NULL; + } + ret->db = db; ret->ref = tokenRef; - ret->owner = - StrDuplicate(JsonValueAsString(HashMapGet(tokenJson, "created_by"))); - ret->name = StrDuplicate(token); - - ret->expires = - JsonValueAsInteger(HashMapGet(tokenJson, "expires_on")); - ret->created = - JsonValueAsInteger(HashMapGet(tokenJson, "created_on")); - - ret->uses = - JsonValueAsInteger(HashMapGet(tokenJson, "uses")); - ret->used = - JsonValueAsInteger(HashMapGet(tokenJson, "used")); - - ret->grants = - UserDecodePrivileges(HashMapGet(tokenJson, "grants")); - return ret; } void -RegTokenFree(RegTokenInfo * tokeninfo) +RegTokenFree(RegTokenInfo *tokeninfo) { if (tokeninfo) { - Free(tokeninfo->name); - Free(tokeninfo->owner); + RegTokenInfoFree(tokeninfo); Free(tokeninfo); } } @@ -169,6 +159,9 @@ RegTokenClose(RegTokenInfo * tokeninfo) return 0; } + /* Write object to database. */ + DbJsonSet(tokeninfo->ref, RegTokenInfoToJson(tokeninfo)); + return DbUnlock(tokeninfo->db, tokeninfo->ref); } static int @@ -200,43 +193,10 @@ RegTokenVerify(char *token) return 1; } -HashMap * -RegTokenJSON(RegTokenInfo * info) -{ - TokenInfo tokinfo; - - if (!info) - { - return NULL; - } - - /* TODO: Consider adding the tokinfo property into - * the RegTokenInfo struct to make that easier. */ - tokinfo.name = info->name; - tokinfo.created_on = info->created; - tokinfo.expires_on = info->expires; - - tokinfo.uses = info->uses; - tokinfo.used = info->used; - - tokinfo.uses = Int64Sub(info->uses, info->used); - if (Int64Eq(info->uses, Int64Neg(Int64Create(0, 1)))) - { - /* If uses == -1(infinite uses), just set it too - * to -1 */ - tokinfo.uses = info->uses; - } - tokinfo.created_by = info->owner; - - - return TokenInfoToJson(&tokinfo); -} - RegTokenInfo * RegTokenCreate(Db * db, char *name, char *owner, UInt64 expires, Int64 uses, int privileges) { RegTokenInfo *ret; - HashMap *tokenJson; UInt64 timestamp = UtilServerTs(); @@ -269,26 +229,12 @@ RegTokenCreate(Db * db, char *name, char *owner, UInt64 expires, Int64 uses, int return NULL; } ret->name = StrDuplicate(name); - ret->owner = StrDuplicate(owner); + ret->created_by = StrDuplicate(owner); ret->used = Int64Create(0, 0); ret->uses = uses; - ret->created = timestamp; - ret->expires = expires; - ret->grants = privileges; - - /* Write user info to database. */ - tokenJson = DbJson(ret->ref); - HashMapSet(tokenJson, "created_by", - JsonValueString(ret->owner)); - HashMapSet(tokenJson, "created_on", - JsonValueInteger(ret->created)); - HashMapSet(tokenJson, "expires_on", - JsonValueInteger(ret->expires)); - HashMapSet(tokenJson, "used", - JsonValueInteger(ret->used)); - HashMapSet(tokenJson, "uses", - JsonValueInteger(ret->uses)); - HashMapSet(tokenJson, "grants", UserEncodePrivileges(privileges)); + ret->created_on = timestamp; + ret->expires_on = expires; + ret->grants = UserEncodePrivileges(privileges); return ret; } diff --git a/src/Routes/RouteAdminTokens.c b/src/Routes/RouteAdminTokens.c index 2f9e768..da9e535 100644 --- a/src/Routes/RouteAdminTokens.c +++ b/src/Routes/RouteAdminTokens.c @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -54,7 +53,7 @@ ROUTE_IMPL(RouteAdminTokens, path, argp) RegTokenInfo *info; - TokenRequest *req; + RegTokenAdminRequest *req; size_t i; @@ -109,9 +108,8 @@ ROUTE_IMPL(RouteAdminTokens, path, argp) HashMap *jsoninfo; info = RegTokenGetInfo(db, tokenname); - jsoninfo = RegTokenJSON(info); + jsoninfo = RegTokenInfoToJson(info); - RegTokenClose(info); RegTokenFree(info); ArrayAdd(tokensarray, JsonValueObject(jsoninfo)); @@ -131,7 +129,7 @@ ROUTE_IMPL(RouteAdminTokens, path, argp) goto finish; } - response = RegTokenJSON(info); + response = RegTokenInfoToJson(info); RegTokenClose(info); RegTokenFree(info); @@ -144,14 +142,14 @@ ROUTE_IMPL(RouteAdminTokens, path, argp) response = MatrixErrorCreate(M_NOT_JSON, NULL); goto finish; } - req = Malloc(sizeof(TokenRequest)); + req = Malloc(sizeof(RegTokenAdminRequest)); req->max_uses = Int64Neg(Int64Create(0, 1)); req->lifetime = Int64Create(0, 0); req->name = NULL; - if (!TokenRequestFromJson(request, req, &msg)) + if (!RegTokenAdminRequestFromJson(request, req, &msg)) { - TokenRequestFree(req); + RegTokenAdminRequestFree(req); Free(req); HttpResponseStatus(args->context, HTTP_BAD_REQUEST); @@ -175,7 +173,7 @@ ROUTE_IMPL(RouteAdminTokens, path, argp) { RegTokenClose(info); RegTokenFree(info); - TokenRequestFree(req); + RegTokenAdminRequestFree(req); Free(req); HttpResponseStatus(args->context, HTTP_BAD_REQUEST); @@ -183,11 +181,11 @@ ROUTE_IMPL(RouteAdminTokens, path, argp) response = MatrixErrorCreate(M_INVALID_PARAM, msg); goto finish; } - response = RegTokenJSON(info); + response = RegTokenInfoToJson(info); RegTokenClose(info); RegTokenFree(info); - TokenRequestFree(req); + RegTokenAdminRequestFree(req); Free(req); break; case HTTP_DELETE: diff --git a/src/Routes/RoutePrivileges.c b/src/Routes/RoutePrivileges.c index 44ca311..addaba0 100644 --- a/src/Routes/RoutePrivileges.c +++ b/src/Routes/RoutePrivileges.c @@ -98,15 +98,15 @@ ROUTE_IMPL(RoutePrivileges, path, argp) switch (HttpRequestMethodGet(args->context)) { case HTTP_POST: - privileges = UserDecodePrivileges(val); + privileges = UserDecodePrivileges(JsonValueAsArray(val)); break; case HTTP_PUT: privileges = UserGetPrivileges(user); - privileges |= UserDecodePrivileges(val); + privileges |= UserDecodePrivileges(JsonValueAsArray(val)); break; case HTTP_DELETE: privileges = UserGetPrivileges(user); - privileges &= ~UserDecodePrivileges(val); + privileges &= ~UserDecodePrivileges(JsonValueAsArray(val)); break; default: /* Impossible */ diff --git a/src/Routes/RouteRegister.c b/src/Routes/RouteRegister.c index 5422c58..3527219 100644 --- a/src/Routes/RouteRegister.c +++ b/src/Routes/RouteRegister.c @@ -276,7 +276,7 @@ ROUTE_IMPL(RouteRegister, path, argp) if (info) { - UserSetPrivileges(user, info->grants); + UserSetPrivileges(user, UserDecodePrivileges(info->grants)); RegTokenClose(info); RegTokenFree(info); } diff --git a/src/User.c b/src/User.c index 02b1e80..effb8ba 100644 --- a/src/User.c +++ b/src/User.c @@ -768,7 +768,7 @@ UserSetPrivileges(User * user, int privileges) return 1; } - val = UserEncodePrivileges(privileges); + val = JsonValueArray(UserEncodePrivileges(privileges)); if (!val) { return 0; @@ -779,31 +779,26 @@ UserSetPrivileges(User * user, int privileges) } int -UserDecodePrivileges(JsonValue * val) +UserDecodePrivileges(Array * arr) { int privileges = USER_NONE; size_t i; - Array *arr; - if (!val) + if (!arr) { goto finish; } - if (JsonValueType(val) == JSON_ARRAY) + for (i = 0; i < ArraySize(arr); i++) { - arr = JsonValueAsArray(val); - for (i = 0; i < ArraySize(arr); i++) + JsonValue *val = ArrayGet(arr, i); + if (!val || JsonValueType(val) != JSON_STRING) { - val = ArrayGet(arr, i); - if (!val || JsonValueType(val) != JSON_STRING) - { - continue; - } - - privileges |= UserDecodePrivilege(JsonValueAsString(val)); + continue; } + + privileges |= UserDecodePrivilege(JsonValueAsString(val)); } finish: @@ -851,7 +846,7 @@ UserDecodePrivilege(const char *p) } } -JsonValue * +Array * UserEncodePrivileges(int privileges) { Array *arr = ArrayCreate(); @@ -883,7 +878,7 @@ UserEncodePrivileges(int privileges) #undef A finish: - return JsonValueArray(arr); + return arr; } UserId * diff --git a/src/include/RegToken.h b/src/include/RegToken.h index 01b5fbd..309a2c2 100644 --- a/src/include/RegToken.h +++ b/src/include/RegToken.h @@ -42,54 +42,7 @@ #include #include -/** - * This structure describes a registration token that is in the - * database. - */ -typedef struct RegTokenInfo -{ - Db *db; - DbRef *ref; - - /* - * The token itself. - */ - char *name; - - /* - * Who created this token. Note that this can be NULL if the - * token was created by Telodendria itself. - */ - char *owner; - - /* - * How many times the token was used. - */ - Int64 used; - - /* - * How many uses are allowed. - */ - Int64 uses; - - /* - * Timestamp when this token was created. - */ - UInt64 created; - - /* - * Timestamp when this token expires, or 0 if it does not - * expire. - */ - UInt64 expires; - - /* - * A bit field describing the privileges this token grants. See - * the User API documentation for the privileges supported. - */ - int grants; - -} RegTokenInfo; +#include /** * ``Use'' the specified registration token by increasing the used @@ -106,12 +59,6 @@ extern void RegTokenUse(RegTokenInfo *); */ extern int RegTokenExists(Db *, char *); -/** - * Returns a JSON object corresponding to a valid TokenInfo (see - * #26) - */ -extern HashMap * RegTokenJSON(RegTokenInfo *); - /** * Delete the specified registration token from the database. */ @@ -138,7 +85,6 @@ RegTokenCreate(Db *, char *, char *, UInt64, Int64, int); * .Fn RegTokenClose . */ extern void RegTokenFree(RegTokenInfo *); - /** * Return a boolean value indicating whether or not the specified token * is valid. A registration token is only valid if it has not expired diff --git a/src/include/User.h b/src/include/User.h index ec266ec..bd30803 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -286,13 +286,13 @@ 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 *); +extern int UserDecodePrivileges(Array *); /** * Encode the packed bit field that represents user privileges as a * JSON value. */ -extern JsonValue *UserEncodePrivileges(int); +extern Array *UserEncodePrivileges(int); /** * Convert a string privilege into its bit in the bit field. This is