sachet/server: auth_required now has more modular permissions
there is no more require_admin, because ADMIN is just one of many permissions
This commit is contained in:
parent
6749391b3c
commit
2fba574773
@ -1,6 +1,6 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from flask.views import MethodView
|
from flask.views import MethodView
|
||||||
from sachet.server.models import ServerSettings
|
from sachet.server.models import ServerSettings, Permissions
|
||||||
from sachet.server import db
|
from sachet.server import db
|
||||||
from sachet.server.views_common import auth_required, ModelAPI
|
from sachet.server.views_common import auth_required, ModelAPI
|
||||||
|
|
||||||
@ -18,17 +18,17 @@ class ServerSettingsAPI(ModelAPI):
|
|||||||
return settings
|
return settings
|
||||||
return rows[-1]
|
return rows[-1]
|
||||||
|
|
||||||
@auth_required(require_admin=True)
|
@auth_required(required_permissions=(Permissions.ADMIN,))
|
||||||
def get(self, auth_user=None):
|
def get(self, auth_user=None):
|
||||||
settings = self.get_settings()
|
settings = self.get_settings()
|
||||||
return super().get(settings)
|
return super().get(settings)
|
||||||
|
|
||||||
@auth_required(require_admin=True)
|
@auth_required(required_permissions=(Permissions.ADMIN,))
|
||||||
def patch(self, auth_user=None):
|
def patch(self, auth_user=None):
|
||||||
settings = self.get_settings()
|
settings = self.get_settings()
|
||||||
return super().patch(settings)
|
return super().patch(settings)
|
||||||
|
|
||||||
@auth_required(require_admin=True)
|
@auth_required(required_permissions=(Permissions.ADMIN,))
|
||||||
def put(self, auth_user=None):
|
def put(self, auth_user=None):
|
||||||
settings = self.get_settings()
|
settings = self.get_settings()
|
||||||
return super().put(settings)
|
return super().put(settings)
|
||||||
|
35
sachet/server/files/views.py
Normal file
35
sachet/server/files/views.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import jwt
|
||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from flask.views import MethodView
|
||||||
|
from sachet.server.models import File, Permissions
|
||||||
|
from sachet.server.views_common import ModelAPI, auth_required
|
||||||
|
|
||||||
|
files_blueprint = Blueprint("files_blueprint", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FilesAPI(ModelAPI):
|
||||||
|
"""Files metadata API."""
|
||||||
|
|
||||||
|
@auth_required
|
||||||
|
def get(self, id, auth_user=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
files_blueprint.add_url_rule(
|
||||||
|
"/files/<id>",
|
||||||
|
view_func=FilesAPI.as_view("files_api"),
|
||||||
|
methods=["POST", "PUT", "PATCH", "GET", "DELETE"],
|
||||||
|
)
|
||||||
|
|
||||||
|
files_blueprint.add_url_rule(
|
||||||
|
"/files/<id>/content",
|
||||||
|
view_func=FilesContentAPI.as_view("files_content_api"),
|
||||||
|
methods=["PUT", "GET"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
users_blueprint.add_url_rule(
|
||||||
|
"/users/<username>",
|
||||||
|
view_func=UserAPI.as_view("user_api"),
|
||||||
|
methods=["GET", "PATCH", "PUT"],
|
||||||
|
)
|
@ -126,12 +126,12 @@ class UserAPI(ModelAPI):
|
|||||||
|
|
||||||
return super().get(info_user)
|
return super().get(info_user)
|
||||||
|
|
||||||
@auth_required(require_admin=True)
|
@auth_required(required_permissions=(Permissions.ADMIN,))
|
||||||
def patch(self, username, auth_user=None):
|
def patch(self, username, auth_user=None):
|
||||||
patch_user = User.query.filter_by(username=username).first()
|
patch_user = User.query.filter_by(username=username).first()
|
||||||
return super().patch(patch_user)
|
return super().patch(patch_user)
|
||||||
|
|
||||||
@auth_required(require_admin=True)
|
@auth_required(required_permissions=(Permissions.ADMIN,))
|
||||||
def put(self, username, auth_user=None):
|
def put(self, username, auth_user=None):
|
||||||
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)
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
from flask.views import MethodView
|
from flask.views import MethodView
|
||||||
from sachet.server.models import Permissions, User, BlacklistToken
|
from sachet.server.models import Permissions, User, BlacklistToken
|
||||||
|
from sachet.server import db
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from marshmallow import ValidationError
|
from marshmallow import ValidationError
|
||||||
|
from bitmask import Bitmask
|
||||||
import jwt
|
import jwt
|
||||||
|
|
||||||
|
|
||||||
def auth_required(func=None, *, require_admin=False):
|
def auth_required(func=None, *, required_permissions=()):
|
||||||
"""Decorator to require authentication.
|
"""Decorator to require authentication.
|
||||||
|
|
||||||
Passes an argument 'user' to the function, with a User object corresponding
|
Passes an argument 'user' to the function, with a User object corresponding
|
||||||
to the authenticated session.
|
to the authenticated session.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
|
||||||
|
required_permissions : tuple of Permissions
|
||||||
|
Permissions required to access this endpoint.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# see https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments
|
# see https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments
|
||||||
@ -44,12 +52,15 @@ def auth_required(func=None, *, require_admin=False):
|
|||||||
401,
|
401,
|
||||||
)
|
)
|
||||||
|
|
||||||
if require_admin and Permissions.ADMIN not in user.permissions:
|
if (
|
||||||
|
Bitmask(AllFlags=Permissions, *required_permissions)
|
||||||
|
not in user.permissions
|
||||||
|
):
|
||||||
return (
|
return (
|
||||||
jsonify(
|
jsonify(
|
||||||
{
|
{
|
||||||
"status": "fail",
|
"status": "fail",
|
||||||
"message": "Administrator permission is required to see this page.",
|
"message": "Missing permissions to access this page.",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
403,
|
403,
|
||||||
@ -112,6 +123,8 @@ class ModelAPI(MethodView):
|
|||||||
for k, v in deserialized.items():
|
for k, v in deserialized.items():
|
||||||
setattr(model, k, v)
|
setattr(model, k, v)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
resp = {
|
resp = {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
}
|
}
|
||||||
@ -138,7 +151,37 @@ class ModelAPI(MethodView):
|
|||||||
for k, v in deserialized.items():
|
for k, v in deserialized.items():
|
||||||
setattr(model, k, v)
|
setattr(model, k, v)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
resp = {
|
resp = {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
}
|
}
|
||||||
return jsonify(resp), 200
|
return jsonify(resp), 200
|
||||||
|
|
||||||
|
def delete(self, model):
|
||||||
|
if not model:
|
||||||
|
resp = {
|
||||||
|
"status": "fail",
|
||||||
|
"message": "This resource does not exist.",
|
||||||
|
}
|
||||||
|
return jsonify(resp), 404
|
||||||
|
|
||||||
|
model.delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({"status": "success"})
|
||||||
|
|
||||||
|
def post(self, ModelClass, data):
|
||||||
|
model_schema = ModelClass.get_schema()
|
||||||
|
|
||||||
|
post_json = request.get_json()
|
||||||
|
try:
|
||||||
|
deserialized = model_schema.load(post_json)
|
||||||
|
except ValidationError as e:
|
||||||
|
resp = {"status": "fail", "message": f"Invalid data: {str(e)}"}
|
||||||
|
return jsonify(resp), 400
|
||||||
|
|
||||||
|
# create new ModelClass instance with all the parameters given in the request
|
||||||
|
model = ModelClass(**deserialized)
|
||||||
|
|
||||||
|
return jsonify({"status": "success"}), 201
|
||||||
|
Loading…
Reference in New Issue
Block a user