From 22cf806220fc0ed89e04f93321612c2292e3533e Mon Sep 17 00:00:00 2001 From: ReAnzu Date: Mon, 26 Feb 2018 21:47:01 -0600 Subject: [PATCH] Added versioning, cleaned up API documentation, fixed endpoints, resolved logout diplay update issue --- API.md | 33 +++++++++++++++++++ client/js/api.js | 25 +++++++------- config.yaml.dist | 1 + server/szurubooru/api/user_token_api.py | 17 +++++++--- server/szurubooru/func/user_tokens.py | 4 +++ .../a39c7f98a7fa_add_user_token_table.py | 1 + server/szurubooru/model/user.py | 1 + 7 files changed, 66 insertions(+), 16 deletions(-) diff --git a/API.md b/API.md index 6d15d551..ae2bb6b3 100644 --- a/API.md +++ b/API.md @@ -60,6 +60,7 @@ - User Tokens - [Listing tokens](#listing-tokens) - [Creating token](#creating-token) + - [Updating token](#updating-token) - [Deleting token](#deleting-token) - Password reset - [Password reset - step 1: mail request](#password-reset---step-2-confirmation) @@ -1535,6 +1536,36 @@ data. Creates a new user token that can be used for authentication of api endpoints instead of a password. + +## Updating user +- **Request** + + `PUT /user-token/` + +- **Input** + + ```json5 + { + "version": , + "enabled": , // optional + } + ``` + +- **Output** + + A [user token resource](#user-token). + +- **Errors** + + - the version is outdated + - the user token does not exist + - privileges are too low + +- **Description** + + Updates an existing user token using specified parameters. All fields + except the [`version`](#versioning) are optional - update concerns only + provided fields. ## Deleting token - **Request** @@ -1806,6 +1837,7 @@ A single user token. "user": , "token": , "enabled": , + "version": , "creationTime": , "lastEditTime": , } @@ -1815,6 +1847,7 @@ A single user token. - ``: micro user. See [micro user](#micro-user). - ``: the token that can be used to authenticate the user. - ``: whether the token is still valid for authentication. +- ``: resource version. See [versioning](#versioning). - ``: time the user token was created , formatted as per RFC 3339. - ``: time the user token was edited, formatted as per RFC 3339. diff --git a/client/js/api.js b/client/js/api.js index 86687cad..67dbdc22 100644 --- a/client/js/api.js +++ b/client/js/api.js @@ -118,9 +118,9 @@ class Api extends events.EventTarget { }); } - get_token(userName, options) { + create_token(userName, options) { return new Promise((resolve, reject) => { - this.post('/user-tokens', {}) + this.post('/user-token', {}) .then(response => { cookies.set( 'auth', @@ -137,15 +137,14 @@ class Api extends events.EventTarget { delete_token(userName, userToken) { return new Promise((resolve, reject) => { - this.delete('/user-tokens/' + userToken, {}) + this.delete('/user-token/' + userToken, {}) .then(response => { const options = {}; cookies.set( 'auth', {'user': userName, 'token': null}, options); - this.userName = userName; - this.userToken = null; + resolve(); }, error => { reject(error); }); @@ -163,7 +162,7 @@ class Api extends events.EventTarget { if (doRemember) { options.expires = 365; } - this.get_token(this.userName, options); + this.create_token(this.userName, options); this.user = response; resolve(); this.dispatchEvent(new CustomEvent('login')); @@ -175,18 +174,20 @@ class Api extends events.EventTarget { } logout() { - this.delete_token(this.userName, this.userToken).then(response => { - this._logout() - }, error => { - this._logout() - }); - + let self = this; + this.delete_token(this.userName, this.userToken) + .then(response => { + self._logout(); + }, error => { + self._logout(); + }); } _logout() { this.user = null; this.userName = null; this.userPassword = null; + this.userToken = null; this.dispatchEvent(new CustomEvent('logout')); } diff --git a/config.yaml.dist b/config.yaml.dist index 0b10dbcf..e3409b6f 100644 --- a/config.yaml.dist +++ b/config.yaml.dist @@ -88,6 +88,7 @@ privileges: 'user_token:list': regular 'user_token:create': regular + 'user_token:edit': regular 'user_token:delete': regular 'posts:create:anonymous': regular diff --git a/server/szurubooru/api/user_token_api.py b/server/szurubooru/api/user_token_api.py index 7559a9fc..bd3037be 100644 --- a/server/szurubooru/api/user_token_api.py +++ b/server/szurubooru/api/user_token_api.py @@ -1,7 +1,7 @@ from typing import Dict from szurubooru import model, rest -from szurubooru.func import auth, user_tokens, serialization +from szurubooru.func import auth, user_tokens, serialization, versions def _serialize( @@ -21,15 +21,24 @@ def get_user_tokens(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Res } -@rest.routes.post('/user-tokens/?') +@rest.routes.post('/user-token/?') def create_user_token(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Response: auth.verify_privilege(ctx.user, 'user_token:create') user_token = user_tokens.create_user_token(ctx.user) return _serialize(ctx, user_token) -@rest.routes.delete('/user-tokens/(?P[^/]+)/?') -def create_user_token(ctx: rest.Context, params: Dict[str, str]) -> rest.Response: +@rest.routes.put('/user-token/(?P[^/]+)/?') +def edit_user_token(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Response: + auth.verify_privilege(ctx.user, 'user_token:edit') + user_token = user_tokens.get_user_token_by_user_and_token(ctx.user, params['user_token']) + versions.verify_version(user_token, ctx) + versions.bump_version(user_token) + return _serialize(ctx, user_token) + + +@rest.routes.delete('/user-token/(?P[^/]+)/?') +def delete_user_token(ctx: rest.Context, params: Dict[str, str]) -> rest.Response: auth.verify_privilege(ctx.user, 'user_token:delete') user_token = user_tokens.get_user_token_by_user_and_token(ctx.user, params['user_token']) if user_token is not None: diff --git a/server/szurubooru/func/user_tokens.py b/server/szurubooru/func/user_tokens.py index 8c9a35de..3492c860 100644 --- a/server/szurubooru/func/user_tokens.py +++ b/server/szurubooru/func/user_tokens.py @@ -20,6 +20,7 @@ class UserTokenSerializer(serialization.BaseSerializer): 'user': self.serialize_user, 'token': self.serialize_token, 'enabled': self.serialize_enabled, + 'version': self.serialize_version, 'creationTime': self.serialize_creation_time, 'lastLoginTime': self.serialize_last_edit_time, } @@ -39,6 +40,9 @@ class UserTokenSerializer(serialization.BaseSerializer): def serialize_enabled(self) -> Any: return self.user_token.enabled + def serialize_version(self) -> Any: + return self.user_token.version + def serialize_user_token( user_token: Optional[model.UserToken], diff --git a/server/szurubooru/migrations/versions/a39c7f98a7fa_add_user_token_table.py b/server/szurubooru/migrations/versions/a39c7f98a7fa_add_user_token_table.py index 6e6ac5d8..c61ee3ea 100644 --- a/server/szurubooru/migrations/versions/a39c7f98a7fa_add_user_token_table.py +++ b/server/szurubooru/migrations/versions/a39c7f98a7fa_add_user_token_table.py @@ -23,6 +23,7 @@ def upgrade(): sa.Column('enabled', sa.Boolean(), nullable=False), sa.Column('creation_time', sa.DateTime(), nullable=False), sa.Column('last_edit_time', sa.DateTime(), nullable=True), + sa.Column('version', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') ) diff --git a/server/szurubooru/model/user.py b/server/szurubooru/model/user.py index 7ca1a10e..3e31c81e 100644 --- a/server/szurubooru/model/user.py +++ b/server/szurubooru/model/user.py @@ -100,5 +100,6 @@ class UserToken(Base): enabled = sa.Column('enabled', sa.Boolean, nullable=False, default=True) creation_time = sa.Column('creation_time', sa.DateTime, nullable=False) last_edit_time = sa.Column('last_edit_time', sa.DateTime) + version = sa.Column('version', sa.Integer, default=1, nullable=False) user = sa.orm.relationship('User')