Compare commits
No commits in common. "88bd79c2284e9830fb08ff4d64072adc2b0f61bd" and "5fd8fada2c23172cced9f49e297a4379242b2c94" have entirely different histories.
88bd79c228
...
5fd8fada2c
15
docs/conf.py
15
docs/conf.py
@ -6,21 +6,22 @@
|
|||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
project = "sachet-server"
|
project = 'sachet-server'
|
||||||
copyright = "2023, dogeystamp"
|
copyright = '2023, dogeystamp'
|
||||||
author = "dogeystamp"
|
author = 'dogeystamp'
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
extensions = ["sphinx_rtd_theme"]
|
extensions = ["sphinx_rtd_theme"]
|
||||||
|
|
||||||
templates_path = ["_templates"]
|
templates_path = ['_templates']
|
||||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
html_theme = "sphinx_rtd_theme"
|
html_theme = 'sphinx_rtd_theme'
|
||||||
html_static_path = ["_static"]
|
html_static_path = ['_static']
|
||||||
|
@ -54,7 +54,6 @@ Sachet implements the following endpoints for this API::
|
|||||||
GET /users/<username>
|
GET /users/<username>
|
||||||
PATCH /users/<username>
|
PATCH /users/<username>
|
||||||
PUT /users/<username>
|
PUT /users/<username>
|
||||||
DELETE /users/<username>
|
|
||||||
|
|
||||||
GET
|
GET
|
||||||
^^^
|
^^^
|
||||||
@ -118,13 +117,6 @@ For example:
|
|||||||
|
|
||||||
Only :ref:`administrators<permissions_table>` can request this method.
|
Only :ref:`administrators<permissions_table>` can request this method.
|
||||||
|
|
||||||
DELETE
|
|
||||||
^^^^^^
|
|
||||||
|
|
||||||
Requesting ``DELETE /users/<username>`` deletes the specified user.
|
|
||||||
|
|
||||||
Only :ref:`administrators<permissions_table>` can request this method.
|
|
||||||
|
|
||||||
.. _user_list_api:
|
.. _user_list_api:
|
||||||
|
|
||||||
List API
|
List API
|
||||||
@ -147,15 +139,3 @@ POST
|
|||||||
|
|
||||||
``POST /users`` creates a new user.
|
``POST /users`` creates a new user.
|
||||||
The request body must conform to the :ref:`User schema<user_schema>`.
|
The request body must conform to the :ref:`User schema<user_schema>`.
|
||||||
|
|
||||||
The server will return a ``201 Created`` code with a similar body to this:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"status": "success",
|
|
||||||
"url": "/users/user"
|
|
||||||
}
|
|
||||||
|
|
||||||
The ``url`` field is the URL to the new user.
|
|
||||||
It can be used in further requests to manage the user's information, or delete it.
|
|
||||||
|
@ -91,7 +91,7 @@ class User(db.Model):
|
|||||||
password, current_app.config.get("BCRYPT_LOG_ROUNDS")
|
password, current_app.config.get("BCRYPT_LOG_ROUNDS")
|
||||||
).decode()
|
).decode()
|
||||||
self.username = username
|
self.username = username
|
||||||
self.url = url_for("users_blueprint.user_api", username=self.username)
|
self.url = url_for("users_blueprint.user_list_api", username=self.username)
|
||||||
self.register_date = datetime.datetime.now()
|
self.register_date = datetime.datetime.now()
|
||||||
|
|
||||||
def encode_token(self, jti=None):
|
def encode_token(self, jti=None):
|
||||||
|
@ -109,7 +109,7 @@ users_blueprint.add_url_rule(
|
|||||||
|
|
||||||
|
|
||||||
class UserAPI(ModelAPI):
|
class UserAPI(ModelAPI):
|
||||||
"""User information API."""
|
"""User information API"""
|
||||||
|
|
||||||
@auth_required
|
@auth_required
|
||||||
def get(self, username, auth_user=None):
|
def get(self, username, auth_user=None):
|
||||||
@ -136,16 +136,11 @@ class UserAPI(ModelAPI):
|
|||||||
put_user = User.query.filter_by(username=username).first()
|
put_user = User.query.filter_by(username=username).first()
|
||||||
return super().put(put_user)
|
return super().put(put_user)
|
||||||
|
|
||||||
@auth_required(required_permissions=(Permissions.ADMIN,))
|
|
||||||
def delete(self, username, auth_user=None):
|
|
||||||
delete_user = User.query.filter_by(username=username).first()
|
|
||||||
return super().delete(delete_user)
|
|
||||||
|
|
||||||
|
|
||||||
users_blueprint.add_url_rule(
|
users_blueprint.add_url_rule(
|
||||||
"/users/<username>",
|
"/users/<username>",
|
||||||
view_func=UserAPI.as_view("user_api"),
|
view_func=UserAPI.as_view("user_api"),
|
||||||
methods=["GET", "PATCH", "PUT", "DELETE"],
|
methods=["GET", "PATCH", "PUT"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,16 +125,6 @@ def users(client):
|
|||||||
Permissions.READ,
|
Permissions.READ,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
no_admin_user=dict(
|
|
||||||
password="password",
|
|
||||||
permissions=Bitmask(
|
|
||||||
Permissions.CREATE,
|
|
||||||
Permissions.MODIFY,
|
|
||||||
Permissions.DELETE,
|
|
||||||
Permissions.LOCK,
|
|
||||||
Permissions.READ,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
administrator=dict(password="4321", permissions=Bitmask(Permissions.ADMIN)),
|
administrator=dict(password="4321", permissions=Bitmask(Permissions.ADMIN)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_post(client, users, auth):
|
|
||||||
"""Test registering a user, then logging in to it."""
|
|
||||||
# register without adequate permissions
|
|
||||||
resp = client.post(
|
|
||||||
"/users",
|
|
||||||
headers=auth("no_admin_user"),
|
|
||||||
json={"username": "claire", "permissions": [], "password": "claire123"},
|
|
||||||
)
|
|
||||||
assert resp.status_code == 403
|
|
||||||
# properly register
|
|
||||||
resp = client.post(
|
|
||||||
"/users",
|
|
||||||
headers=auth("administrator"),
|
|
||||||
json={"username": "claire", "permissions": [], "password": "claire123"},
|
|
||||||
)
|
|
||||||
assert resp.status_code == 201
|
|
||||||
data = resp.get_json()
|
|
||||||
url = data.get("url")
|
|
||||||
assert url is not None
|
|
||||||
assert url == "/users/claire"
|
|
||||||
|
|
||||||
# try logging in now
|
|
||||||
resp = client.post(
|
|
||||||
"/users/login", json={"username": "claire", "password": "claire123"}
|
|
||||||
)
|
|
||||||
assert resp.status_code == 200
|
|
||||||
data = resp.get_json()
|
|
||||||
assert data.get("status") == "success"
|
|
||||||
assert data.get("username") == "claire"
|
|
||||||
token = data.get("auth_token")
|
|
||||||
assert token is not None and token != ""
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete(client, users, auth):
|
|
||||||
"""Test registering a user, then deleting it."""
|
|
||||||
resp = client.post(
|
|
||||||
"/users",
|
|
||||||
headers=auth("administrator"),
|
|
||||||
json={"username": "claire", "permissions": [], "password": "claire123"},
|
|
||||||
)
|
|
||||||
assert resp.status_code == 201
|
|
||||||
|
|
||||||
# try logging in now
|
|
||||||
resp = client.post(
|
|
||||||
"/users/login", json={"username": "claire", "password": "claire123"}
|
|
||||||
)
|
|
||||||
assert resp.status_code == 200
|
|
||||||
data = resp.get_json()
|
|
||||||
token = data.get("auth_token")
|
|
||||||
|
|
||||||
# test if the token works
|
|
||||||
resp = client.get("/users/claire", headers={"Authorization": f"bearer {token}"})
|
|
||||||
assert resp.status_code == 200
|
|
||||||
|
|
||||||
# delete without permission
|
|
||||||
resp = client.delete("/users/claire", headers=auth("no_admin_user"))
|
|
||||||
assert resp.status_code == 403
|
|
||||||
|
|
||||||
# delete properly
|
|
||||||
resp = client.delete("/users/claire", headers=auth("administrator"))
|
|
||||||
assert resp.status_code == 200
|
|
||||||
|
|
||||||
# test if the token for a non-existent user works
|
|
||||||
resp = client.get("/users/claire", headers={"Authentication": f"bearer {token}"})
|
|
||||||
assert resp.status_code == 401
|
|
Loading…
x
Reference in New Issue
Block a user