From 3b50112c1052fcff7550078ca7360adadc15eb9b Mon Sep 17 00:00:00 2001 From: grimhilt Date: Tue, 12 Sep 2023 15:08:54 +0200 Subject: [PATCH] add permissions to files api --- src/api/__init__.py | 3 +- src/api/controllers/file.py | 11 +++++++ src/api/dao/Playlist.py | 29 ++++++++++++++++++ src/api/dao/UsersDao.py | 24 +++++++++++++++ src/api/permissions.py | 60 +++++++++++++++++++++---------------- src/config/config.py | 2 +- 6 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 src/api/dao/UsersDao.py diff --git a/src/api/__init__.py b/src/api/__init__.py index 1950571..b6e548d 100644 --- a/src/api/__init__.py +++ b/src/api/__init__.py @@ -3,6 +3,7 @@ from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS from flask_login import LoginManager from os import path +from config.config import get_secret_key import logging @@ -15,7 +16,7 @@ def create_api(): CORS(app) logging.getLogger('flask_cors').level = logging.DEBUG app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_NAME}' - app.secret_key = b'_5#y2L"F4Qfj8zxec]' + app.secret_key = get_secret_key() login_manager = LoginManager() login_manager.init_app(app) diff --git a/src/api/controllers/file.py b/src/api/controllers/file.py index 41f03d0..4aa6da5 100644 --- a/src/api/controllers/file.py +++ b/src/api/controllers/file.py @@ -1,4 +1,6 @@ from flask import Blueprint, request, jsonify, send_file +from flask_login import login_required +from ..permissions import Perm, permissions from ..models import File from .. import db @@ -6,6 +8,8 @@ files = Blueprint('files', __name__) FILE_DIR = './data/' @files.route('/files', methods=['POST']) +@login_required +@permissions.require([Perm.EDIT_PLAYLIST]) def upload(): res = [] for file_key in request.files: @@ -22,6 +26,8 @@ def upload(): return jsonify(res) @files.route('/files', methods=['GET']) +@login_required +@permissions.require([Perm.EDIT_PLAYLIST]) def list(): files = db.session.query(File).all() res = [] @@ -30,12 +36,17 @@ def list(): return jsonify(res) @files.route('/files/', methods=['GET']) +@login_required +@permissions.require([Perm.VIEW_PLAYLIST]) def load(file_id): file = db.session.query(File).filter(File.id == file_id).first() return send_file(('../../data/' + file.name), mimetype=file.type) @files.route('/files/', methods=['DELETE']) +@login_required +@permissions.require([Perm.OWN_PLAYLIST]) def delete(file_id): + # todo warning if file is still in use rows = db.session.query(File).filter(File.id == file_id).all() for row in rows: db.session.delete(row) diff --git a/src/api/dao/Playlist.py b/src/api/dao/Playlist.py index ae23584..169037d 100644 --- a/src/api/dao/Playlist.py +++ b/src/api/dao/Playlist.py @@ -13,3 +13,32 @@ class PlaylistDao: files.append(file) return (query, files) + + def get_playlist_q(playlist_id): + query = db.session.query(Playlist).filter(Playlist.id == playlist_id).first() + return query + + def has_role_view_d(playlist_id, user_id): + has_role_to_view = db.session.query(Playlist) \ + .filter(Playlist.id == playlist_id) \ + .filter( \ + Playlist.view.any( \ + # check if a role belongs to this user + Role.user_id == user_id or \ + # check if a this user has a role to view + Role.users.any(User.id == user_id) \ + )) \ + .first() + return has_role_to_view + + def has_role_view_d(playlist_id, user_id): + has_role_to_edit = db.session.query(Playlist) \ + .filter( \ + Playlist.edit.any( \ + # check if a role belongs to this user + Role.user_id == user_id or \ + # check if a this user has a role to edit + Role.users.any(User.id == user_id) \ + )) \ + .first() + return has_role_to_edit diff --git a/src/api/dao/UsersDao.py b/src/api/dao/UsersDao.py new file mode 100644 index 0000000..d346b7d --- /dev/null +++ b/src/api/dao/UsersDao.py @@ -0,0 +1,24 @@ +from .. import db +from ..models import User, Role + +class UsersDao: + def has_role_view_q(user_id): + has_role_to_view = db.session.query(User) \ + .filter(User.id == user_id) \ + .filter( \ + User.roles.any( \ + Role.users.any(Role.playlists_view is not None) \ + )) \ + .first() + return has_role_to_view + + def has_role_edit_q(user_id): + has_role_to_edit = db.session.query(User) \ + .filter(User.id == user_id) \ + .filter( \ + User.roles.any( \ + Role.users.any(Role.playlists_edit is not None) \ + )) \ + .first() + return has_role_to_edit + diff --git a/src/api/permissions.py b/src/api/permissions.py index 55ccf61..0f2fb07 100644 --- a/src/api/permissions.py +++ b/src/api/permissions.py @@ -4,6 +4,9 @@ from flask import request, jsonify from flask_login import current_user from . import db from .models import Playlist, PlaylistFile, User, Role, UserRole +from .dao.Playlist import PlaylistDao +from .dao.UsersDao import UsersDao + class Perm(IntEnum): CREATE_USER = 0 @@ -59,11 +62,10 @@ def CheckPermissionFactory(perm): def get_playlist_id(args): if 'playlist_id' in args: return args['playlist_id'] - json = request.get_json() - if 'playlist_id' in json: - print("in") + json = request.get_json(silent=True) + if json is not None and 'playlist_id' in json: return json['playlist_id'] - return + return None def checkBit(permissions, index): binStr = bin(permissions) @@ -84,12 +86,14 @@ class CheckOwnPlaylist: def is_valid(self, args): playlist_id = get_playlist_id(args) - query = db.session.query(Playlist).filter(Playlist.id == playlist_id).first() + if playlist_id is None: + return False + + query = PlaylistDao.get_playlist_q(playlist_id) if query is None: self.message = "This playlist doesn't exist" self.status_code = 404 return False - print(query.as_dict()) return query.as_dict()['owner_id'] == current_user.as_dict()['id'] class CheckViewPlaylist: @@ -109,15 +113,18 @@ class CheckViewPlaylist: playlist_id = get_playlist_id(args) user_id = current_user.as_dict()['id'] - has_role_to_view = db.session.query(Playlist) \ - .filter( \ - Playlist.view.any( \ - # check if a role belongs to this user - Role.user_id == user_id or \ - # check if a this user has a role to view - Role.users.any(User.id == user_id) \ - )) \ - .first() + + # if playlist_id is none then there is not precise playlist + # to compare the permissions, so we check if the user has + # a permission on any playlist + has_role_to_view = None + if playlist_id is not None: + # check if has role on one precise playlist + has_role_to_view = PlaylistDao.has_role_to_view(playlist_id, user_id) + else: + # check if has role to view any playlist + has_role_to_view = UsersDao.has_role_view_q(user_id) + return has_role_to_view is not None class CheckEditPlaylist: @@ -133,18 +140,21 @@ class CheckEditPlaylist: self.message = "This playlist doesn't exist" self.status_code = 404 return False - + playlist_id = get_playlist_id(args) user_id = current_user.as_dict()['id'] - has_role_to_edit = db.session.query(Playlist) \ - .filter( \ - Playlist.edit.any( \ - # check if a role belongs to this user - Role.user_id == user_id or \ - # check if a this user has a role to edit - Role.users.any(User.id == user_id) \ - )) \ - .first() + + # if playlist_id is none then there is not precise playlist + # to compare the permissions, so we check if the user has + # a permission on any playlist + has_role_to_edit = None + if playlist_id is not None: + # check if has role on one precise playlist + has_role_to_edit = PlaylistDao.has_role_to_edit(playlist_id, user_id) + else: + # check if has role to view any playlist + has_role_to_edit = UsersDao.has_role_edit_q(user_id) + return has_role_to_edit is not None class CheckCreateUser: diff --git a/src/config/config.py b/src/config/config.py index 5fdab08..ed2638b 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -2,7 +2,7 @@ import os import random import string -SECRET_KEY_FILE = './src/config/SECRET_KEY' +SECRET_KEY_FILE = './config/SECRET_KEY' def get_secret_key(): if os.path.isfile(SECRET_KEY_FILE):