From 743c8b9eaa332dc97ee5df1b5d87c9e1f11e3c22 Mon Sep 17 00:00:00 2001 From: dogeystamp Date: Sun, 2 Apr 2023 14:00:11 -0400 Subject: [PATCH] added stub for server settings we can now modify the default permissions for unauthenticated users it doesn't do anything yet, though --- requirements.txt | 2 +- sachet/server/__init__.py | 4 ++ sachet/server/admin/__init__.py | 0 sachet/server/admin/views.py | 41 ++++++++++++ sachet/server/models.py | 23 ++++++- tests/test_serversettings.py | 111 ++++++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 sachet/server/admin/__init__.py create mode 100644 sachet/server/admin/views.py create mode 100644 tests/test_serversettings.py diff --git a/requirements.txt b/requirements.txt index 94b3771..6de3da3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs==22.2.0 bcrypt==4.0.1 -bitmask @ git+https://github.com/dogeystamp/bitmask@8524113fcdc22a570bda77d440374f5f269fdb79 +bitmask @ git+https://github.com/dogeystamp/bitmask@e3726f069c24f1db6ecb2e3d3143c9c930c83fa5 black==23.3.0 click==8.1.3 coverage==7.2.1 diff --git a/sachet/server/__init__.py b/sachet/server/__init__.py index 27023af..7edd62b 100644 --- a/sachet/server/__init__.py +++ b/sachet/server/__init__.py @@ -27,5 +27,9 @@ from sachet.server.users.views import users_blueprint app.register_blueprint(users_blueprint) +from sachet.server.admin.views import admin_blueprint + +app.register_blueprint(admin_blueprint) + with app.app_context(): db.create_all() diff --git a/sachet/server/admin/__init__.py b/sachet/server/admin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sachet/server/admin/views.py b/sachet/server/admin/views.py new file mode 100644 index 0000000..b91fb5e --- /dev/null +++ b/sachet/server/admin/views.py @@ -0,0 +1,41 @@ +from flask import Blueprint, request, jsonify +from flask.views import MethodView +from sachet.server.models import ServerSettings +from sachet.server import db +from sachet.server.views_common import auth_required, ModelAPI + + +admin_blueprint = Blueprint("admin_blueprint", __name__) + + +class ServerSettingsAPI(ModelAPI): + def get_settings(self): + rows = ServerSettings.query.all() + if len(rows) == 0: + settings = ServerSettings() + db.session.add(settings) + db.session.commit() + return settings + return rows[-1] + + @auth_required(require_admin=True) + def get(self, auth_user=None): + settings = self.get_settings() + return super().get(settings) + + @auth_required(require_admin=True) + def patch(self, auth_user=None): + settings = self.get_settings() + return super().patch(settings) + + @auth_required(require_admin=True) + def put(self, auth_user=None): + settings = self.get_settings() + return super().put(settings) + + +admin_blueprint.add_url_rule( + "/admin/settings", + view_func=ServerSettingsAPI.as_view("server_settings_api"), + methods=["PATCH", "GET", "PUT"], +) diff --git a/sachet/server/models.py b/sachet/server/models.py index 0a3edf2..931334b 100644 --- a/sachet/server/models.py +++ b/sachet/server/models.py @@ -129,7 +129,7 @@ class User(db.Model): username = ma.auto_field() register_date = ma.auto_field() - permissions = PermissionField(data_key="permissions") + permissions = PermissionField() return Schema() @@ -167,3 +167,24 @@ class BlacklistToken(db.Model): if entry.expires < datetime.datetime.utcnow(): db.session.delete(entry) return True + + +class ServerSettings(db.Model): + __tablename__ = "server_settings" + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + + default_permissions_number = db.Column(db.BigInteger, nullable=False, default=0) + default_permissions = PermissionProperty() + + def __init__(self, default_permissions=Bitmask(AllFlags=Permissions)): + self.default_permissions = default_permissions + + def get_schema(self): + class Schema(ma.SQLAlchemySchema): + class Meta: + model = self + + default_permissions = PermissionField() + + return Schema() diff --git a/tests/test_serversettings.py b/tests/test_serversettings.py new file mode 100644 index 0000000..09f2750 --- /dev/null +++ b/tests/test_serversettings.py @@ -0,0 +1,111 @@ +from bitmask import Bitmask +from sachet.server.models import Permissions, ServerSettings + +server_settings_schema = ServerSettings.get_schema(ServerSettings) + + +def test_default_perms(client, tokens): + """Test the default permissions.""" + + # try with regular user to make sure it doesn't work + resp = client.get( + "/admin/settings", + headers={"Authorization": f"bearer {tokens['jeff']}"}, + ) + assert resp.status_code == 403 + + resp = client.get( + "/admin/settings", + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 200 + + assert server_settings_schema.load(resp.get_json()) == dict( + default_permissions=Bitmask(AllFlags=Permissions) + ) + + +def test_patch_perms(client, tokens): + """Test the PATCH endpoint for default server permissions.""" + + # try with regular user to make sure it doesn't work + resp = client.patch( + "/admin/settings", + json={"default_permissions": ["ADMIN"]}, + headers={"Authorization": f"bearer {tokens['jeff']}"}, + ) + assert resp.status_code == 403 + + # test malformed patch + resp = client.patch( + "/admin/settings", + json="hurr durr", + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 400 + + resp = client.patch( + "/admin/settings", + json={"default_permissions": ["ADMIN"]}, + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 200 + + # request new info + resp = client.get( + "/admin/settings", + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 200 + + assert server_settings_schema.load(resp.get_json()) == dict( + default_permissions=Bitmask(Permissions.ADMIN) + ) + + +def test_put_perms(client, tokens): + """Test the PUT endpoint for default server permissions.""" + + # try with regular user to make sure it doesn't work + resp = client.put( + "/admin/settings", + json={"default_permissions": ["ADMIN"]}, + headers={"Authorization": f"bearer {tokens['jeff']}"}, + ) + assert resp.status_code == 403 + + # test malformed put + resp = client.put( + "/admin/settings", + json="hurr durr", + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 400 + + # request current info (that we'll modify before putting back) + resp = client.get( + "/admin/settings", + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 200 + + data = resp.get_json() + data["default_permissions"] = ["ADMIN"] + + resp = client.put( + "/admin/settings", + json=data, + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 200 + + # request new info + resp = client.get( + "/admin/settings", + headers={"Authorization": f"bearer {tokens['administrator']}"}, + ) + assert resp.status_code == 200 + + assert server_settings_schema.load(resp.get_json()) == dict( + default_permissions=Bitmask(Permissions.ADMIN) + )