Added versioning, cleaned up API documentation, fixed endpoints, resolved logout diplay update issue
This commit is contained in:
parent
d0b423e91c
commit
22cf806220
7 changed files with 66 additions and 16 deletions
33
API.md
33
API.md
|
@ -60,6 +60,7 @@
|
||||||
- User Tokens
|
- User Tokens
|
||||||
- [Listing tokens](#listing-tokens)
|
- [Listing tokens](#listing-tokens)
|
||||||
- [Creating token](#creating-token)
|
- [Creating token](#creating-token)
|
||||||
|
- [Updating token](#updating-token)
|
||||||
- [Deleting token](#deleting-token)
|
- [Deleting token](#deleting-token)
|
||||||
- Password reset
|
- Password reset
|
||||||
- [Password reset - step 1: mail request](#password-reset---step-2-confirmation)
|
- [Password reset - step 1: mail request](#password-reset---step-2-confirmation)
|
||||||
|
@ -1536,6 +1537,36 @@ data.
|
||||||
Creates a new user token that can be used for authentication of api
|
Creates a new user token that can be used for authentication of api
|
||||||
endpoints instead of a password.
|
endpoints instead of a password.
|
||||||
|
|
||||||
|
## Updating user
|
||||||
|
- **Request**
|
||||||
|
|
||||||
|
`PUT /user-token/<token>`
|
||||||
|
|
||||||
|
- **Input**
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"version": <version>,
|
||||||
|
"enabled": <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
|
## Deleting token
|
||||||
- **Request**
|
- **Request**
|
||||||
|
|
||||||
|
@ -1806,6 +1837,7 @@ A single user token.
|
||||||
"user": <user>,
|
"user": <user>,
|
||||||
"token": <token>,
|
"token": <token>,
|
||||||
"enabled": <enabled>,
|
"enabled": <enabled>,
|
||||||
|
"version": <version>,
|
||||||
"creationTime": <creation-time>,
|
"creationTime": <creation-time>,
|
||||||
"lastEditTime": <last-edit-time>,
|
"lastEditTime": <last-edit-time>,
|
||||||
}
|
}
|
||||||
|
@ -1815,6 +1847,7 @@ A single user token.
|
||||||
- `<user>`: micro user. See [micro user](#micro-user).
|
- `<user>`: micro user. See [micro user](#micro-user).
|
||||||
- `<token>`: the token that can be used to authenticate the user.
|
- `<token>`: the token that can be used to authenticate the user.
|
||||||
- `<enabled>`: whether the token is still valid for authentication.
|
- `<enabled>`: whether the token is still valid for authentication.
|
||||||
|
- `<version>`: resource version. See [versioning](#versioning).
|
||||||
- `<creation-time>`: time the user token was created , formatted as per RFC 3339.
|
- `<creation-time>`: time the user token was created , formatted as per RFC 3339.
|
||||||
- `<last-edit-time>`: time the user token was edited, formatted as per RFC 3339.
|
- `<last-edit-time>`: time the user token was edited, formatted as per RFC 3339.
|
||||||
|
|
||||||
|
|
|
@ -118,9 +118,9 @@ class Api extends events.EventTarget {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get_token(userName, options) {
|
create_token(userName, options) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.post('/user-tokens', {})
|
this.post('/user-token', {})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
cookies.set(
|
cookies.set(
|
||||||
'auth',
|
'auth',
|
||||||
|
@ -137,15 +137,14 @@ class Api extends events.EventTarget {
|
||||||
|
|
||||||
delete_token(userName, userToken) {
|
delete_token(userName, userToken) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.delete('/user-tokens/' + userToken, {})
|
this.delete('/user-token/' + userToken, {})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const options = {};
|
const options = {};
|
||||||
cookies.set(
|
cookies.set(
|
||||||
'auth',
|
'auth',
|
||||||
{'user': userName, 'token': null},
|
{'user': userName, 'token': null},
|
||||||
options);
|
options);
|
||||||
this.userName = userName;
|
resolve();
|
||||||
this.userToken = null;
|
|
||||||
}, error => {
|
}, error => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
|
@ -163,7 +162,7 @@ class Api extends events.EventTarget {
|
||||||
if (doRemember) {
|
if (doRemember) {
|
||||||
options.expires = 365;
|
options.expires = 365;
|
||||||
}
|
}
|
||||||
this.get_token(this.userName, options);
|
this.create_token(this.userName, options);
|
||||||
this.user = response;
|
this.user = response;
|
||||||
resolve();
|
resolve();
|
||||||
this.dispatchEvent(new CustomEvent('login'));
|
this.dispatchEvent(new CustomEvent('login'));
|
||||||
|
@ -175,18 +174,20 @@ class Api extends events.EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
this.delete_token(this.userName, this.userToken).then(response => {
|
let self = this;
|
||||||
this._logout()
|
this.delete_token(this.userName, this.userToken)
|
||||||
|
.then(response => {
|
||||||
|
self._logout();
|
||||||
}, error => {
|
}, error => {
|
||||||
this._logout()
|
self._logout();
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_logout() {
|
_logout() {
|
||||||
this.user = null;
|
this.user = null;
|
||||||
this.userName = null;
|
this.userName = null;
|
||||||
this.userPassword = null;
|
this.userPassword = null;
|
||||||
|
this.userToken = null;
|
||||||
this.dispatchEvent(new CustomEvent('logout'));
|
this.dispatchEvent(new CustomEvent('logout'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ privileges:
|
||||||
|
|
||||||
'user_token:list': regular
|
'user_token:list': regular
|
||||||
'user_token:create': regular
|
'user_token:create': regular
|
||||||
|
'user_token:edit': regular
|
||||||
'user_token:delete': regular
|
'user_token:delete': regular
|
||||||
|
|
||||||
'posts:create:anonymous': regular
|
'posts:create:anonymous': regular
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from szurubooru import model, rest
|
from szurubooru import model, rest
|
||||||
from szurubooru.func import auth, user_tokens, serialization
|
from szurubooru.func import auth, user_tokens, serialization, versions
|
||||||
|
|
||||||
|
|
||||||
def _serialize(
|
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:
|
def create_user_token(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Response:
|
||||||
auth.verify_privilege(ctx.user, 'user_token:create')
|
auth.verify_privilege(ctx.user, 'user_token:create')
|
||||||
user_token = user_tokens.create_user_token(ctx.user)
|
user_token = user_tokens.create_user_token(ctx.user)
|
||||||
return _serialize(ctx, user_token)
|
return _serialize(ctx, user_token)
|
||||||
|
|
||||||
|
|
||||||
@rest.routes.delete('/user-tokens/(?P<user_token>[^/]+)/?')
|
@rest.routes.put('/user-token/(?P<user_token>[^/]+)/?')
|
||||||
def create_user_token(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
|
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<user_token>[^/]+)/?')
|
||||||
|
def delete_user_token(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
|
||||||
auth.verify_privilege(ctx.user, 'user_token:delete')
|
auth.verify_privilege(ctx.user, 'user_token:delete')
|
||||||
user_token = user_tokens.get_user_token_by_user_and_token(ctx.user, params['user_token'])
|
user_token = user_tokens.get_user_token_by_user_and_token(ctx.user, params['user_token'])
|
||||||
if user_token is not None:
|
if user_token is not None:
|
||||||
|
|
|
@ -20,6 +20,7 @@ class UserTokenSerializer(serialization.BaseSerializer):
|
||||||
'user': self.serialize_user,
|
'user': self.serialize_user,
|
||||||
'token': self.serialize_token,
|
'token': self.serialize_token,
|
||||||
'enabled': self.serialize_enabled,
|
'enabled': self.serialize_enabled,
|
||||||
|
'version': self.serialize_version,
|
||||||
'creationTime': self.serialize_creation_time,
|
'creationTime': self.serialize_creation_time,
|
||||||
'lastLoginTime': self.serialize_last_edit_time,
|
'lastLoginTime': self.serialize_last_edit_time,
|
||||||
}
|
}
|
||||||
|
@ -39,6 +40,9 @@ class UserTokenSerializer(serialization.BaseSerializer):
|
||||||
def serialize_enabled(self) -> Any:
|
def serialize_enabled(self) -> Any:
|
||||||
return self.user_token.enabled
|
return self.user_token.enabled
|
||||||
|
|
||||||
|
def serialize_version(self) -> Any:
|
||||||
|
return self.user_token.version
|
||||||
|
|
||||||
|
|
||||||
def serialize_user_token(
|
def serialize_user_token(
|
||||||
user_token: Optional[model.UserToken],
|
user_token: Optional[model.UserToken],
|
||||||
|
|
|
@ -23,6 +23,7 @@ def upgrade():
|
||||||
sa.Column('enabled', sa.Boolean(), nullable=False),
|
sa.Column('enabled', sa.Boolean(), nullable=False),
|
||||||
sa.Column('creation_time', sa.DateTime(), nullable=False),
|
sa.Column('creation_time', sa.DateTime(), nullable=False),
|
||||||
sa.Column('last_edit_time', sa.DateTime(), nullable=True),
|
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.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint('id')
|
||||||
)
|
)
|
||||||
|
|
|
@ -100,5 +100,6 @@ class UserToken(Base):
|
||||||
enabled = sa.Column('enabled', sa.Boolean, nullable=False, default=True)
|
enabled = sa.Column('enabled', sa.Boolean, nullable=False, default=True)
|
||||||
creation_time = sa.Column('creation_time', sa.DateTime, nullable=False)
|
creation_time = sa.Column('creation_time', sa.DateTime, nullable=False)
|
||||||
last_edit_time = sa.Column('last_edit_time', sa.DateTime)
|
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')
|
user = sa.orm.relationship('User')
|
||||||
|
|
Loading…
Reference in a new issue