#!/usr/bin/env python3 # -*- coding: utf-8 -*- import flask import flask_login as fll import datetime from db import db, File, User, levels_users import settings from werkzeug.utils import secure_filename import os import os.path _here = os.path.dirname(os.path.realpath(__file__)) login_manager = fll.LoginManager() app = flask.Flask(__name__) app.config.from_pyfile('settings.cfg') app.config['UPLOAD_FOLDER'] = settings.UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH'] = settings.MAX_FILE_SIZE * 1024 * 1024 ####################################################################### # Plugin management # ####################################################################### # Login manager login_manager.init_app(app) # Database management db.app = app db.init_app(app) db.create_all() db.session.commit() # Insert admin if not exist if User.query.limit(1).first() is None: admin = User(login='admin', name='Admin', is_admin=levels_users['Admin']) admin.set_password('admin') db.session.add(admin) db.session.commit() # Login management @login_manager.user_loader def load_user(user_id): if user_id is None : return None return User.query.get(user_id) @login_manager.unauthorized_handler def unauthorized_callback(): return flask.redirect('/login?next=' + flask.request.path) ####################################################################### # Global variables or fuctions # ####################################################################### URL_DEFAULT = '/myrepo' @app.route('/') def home(): user = fll.current_user if not hasattr(user, 'is_admin'): user = None return flask.render_template("index.html", user=user) @app.route('/favicon.ico') def favicon(): return flask.redirect('/static/favicon.ico') ####################################################################### # Login, logout # ####################################################################### @app.route('/login', methods=['GET', 'POST']) def login(): if fll.current_user.is_authenticated: return flask.redirect(URL_DEFAULT) if flask.request.method=="POST": username = flask.request.form['username'] password = flask.request.form['password'] user = User.query.get(username) if user and user.is_admin > levels_users['Inactive'] and user.check_password(password): fll.login_user(user) next = flask.request.args.get('next') if next is not None and next[0]=='/': return flask.redirect(next) else: return flask.redirect(URL_DEFAULT) else: app.logger.warning('Login fail for user {} from {}'.format(user, flask.request.remote_addr)) flask.flash('Nom d\'utilisateur ou mot de passe incorrect') return flask.render_template('login.html', user=None) @app.route('/logout') def logout(): fll.logout_user() return flask.redirect('/') ####################################################################### # Upload form # ####################################################################### def check_filename_in_db(filename, user): exist = File.query.filter_by(name=filename, user=user.login).first() return exist is not None def check_filename(filename, user): filepath = genpath(filename, user) if os.path.lexists(filepath) or check_filename_in_db(filename, user): fn, ext = os.path.splitext(filename) i = 0 if settings.NMAX_EXTENSION == 0: return None filename = '{}_{:04d}{}'.format(fn, i, ext) while os.path.lexists(genpath(filename, user)) or check_filename_in_db(filename, user): i+=1 filename = '{}_{:04d}{}'.format(fn, i, ext) if i > settings.NMAX_EXTENSION: break else: return filename return None return filename def genpath(filename, user): if isinstance(user, str): username = user else: username = user.login return os.path.join(app.config['UPLOAD_FOLDER'], username, filename) @app.route('/up', methods=['GET', 'POST']) def uploaderpage(): user = fll.current_user if not hasattr(user, 'is_admin') or user.is_admin < levels_users['Simple user']: user = None if not flask.request.method=="POST": return flask.render_template('up.html', user=user) if flask.request.method=="POST" : # Parse global things if 'graphical' in flask.request.form and flask.request.form['graphical'] == '1': graphical = True else: graphical = False if 'visible' in flask.request.form and '1' in flask.request.form['visible']: visible = True else: visible = False if 'filepwd' in flask.request.form and len(flask.request.form['filepwd'])>0: filepwd = flask.request.form['filepwd'] else: filepwd = None # Check auth first if user is None: print(flask.request.form) if 'username' not in flask.request.form or 'password' not in flask.request.form: return returnuploader(graphical, 'ERR: You have to provide creedentials or log in', None) username = flask.request.form['username'] password = flask.request.form['password'] user = User.query.get(username) if not user or user.is_admin <= levels_users['Inactive'] or not user.check_password(password): return returnuploader(graphical, 'ERR: Incorrect login credentials', None) fll.login_user(user) # When auth ok, check for files ! if 'file[]' not in flask.request.files: return returnuploader(graphical, 'ERR: File have to be provided (file[] field)', user) lfiles = flask.request.files.getlist('file[]') lnewnames = flask.request.form.getlist('filename[]') ldesc = flask.request.form.getlist('desc[]') done = 0 ret="OK\n" for i in range(len(lfiles)): # if user does not select file, browser also # submit an empty part without filename f = lfiles[i] if f.filename == '': continue if i0: name = lnewnames[i] else: name = f.filename if i0: desc = ldesc[i] else: desc = '' name = secure_filename(name) name1 = check_filename(name, user) if name1 is None: return returnuploader(graphical, 'ERR: Already existing name {}'.format(name), user) # Add to database toadd = File(name=name1, user=user.login, visible=visible, date=datetime.datetime.now(), desc=desc) toadd.set_password(filepwd) db.session.add(toadd) db.session.commit() # Save it to disk path = genpath(name1, user) try: if not os.path.isdir(os.path.dirname(path)): os.mkdir(os.path.dirname(path)) f.save(path) ret += "/d/{}/{}\n".format(user.login, name1) except IOError as err: return returnuploader(graphical, 'ERR: Something went wrong when trying to write file to disk', user) done += 1 # return if done == 0: return returnuploader(graphical, 'ERR: You have to provide at least one valid file', user) if graphical: return flask.redirect(URL_DEFAULT) else: return ret def returnuploader(graphical, info, user): """ Function to create return value for uploaderpage function If graphical is True, return the web page, otherwise return simple text graphical:bool: it is graphical session ? info:str: text to send user:User instance or None if not logged in """ if not graphical: return info else: flask.flash(info) return flask.render_template('up.html', user=user) ####################################################################### # Download pages # ####################################################################### @app.route('/d//', defaults={'raw': False}, methods=['GET', 'POST']) @app.route('/d///raw', defaults = {'raw': True}, methods=['GET', 'POST']) def dl(user, filename, raw=False): cuser = fll.current_user if not hasattr(cuser, 'is_admin') or cuser.is_admin < levels_users['Simple user']: cuser = None fl = File.query.filter_by(name=filename, user=user).first() if fl is None: return flask.abort(404) pwd = None if flask.request.method=="POST": if 'password' in flask.request.form: pwd = flask.request.form['password'] if fl.check_password(pwd): path = genpath(filename, user) folder = os.path.dirname(path) fn = os.path.basename(path) if raw: return flask.send_from_directory(folder, filename=fn, as_attachment=False) else: return flask.send_from_directory(folder, filename=fn, as_attachment=False) else: if raw: return 'ERR: Incorrect password' else: if flask.request.method == "POST": flask.flash('Incorrect password') return flask.render_template('dl_pwd.html', user=cuser, filename=filename) pass ####################################################################### # Show repos # ####################################################################### @app.route('/myrepo') @fll.login_required def myrepo(): return flask.redirect('/d/{}'.format(fll.current_user.login)) @app.route('/d/') def repo(user): user = User.query.get(user) if user is None: return flask.abort(404) cuser = fll.current_user if not hasattr(cuser, 'is_admin') or cuser.is_admin < levels_users['Simple user']: cuser = None ismine = user.login == cuser.login if cuser is not None else False isadmin = cuser.is_admin>=levels_users['Admin'] or ismine if cuser is not None else False if isadmin: l = File.query.filter_by(user=user.login).all() else: l = File.query.filter_by(user=user.login, visible=True).all() return flask.render_template('repo.html', user=cuser, isadmin=isadmin, ismine=ismine, folder=user.login, l=l) ####################################################################### # Edit files # ####################################################################### @app.route('/f//edit', methods=['GET', 'POST']) @fll.login_required def editfile(id): user = fll.current_user f = File.query.get(id) if f is None: return flask.redirect(URL_DEFAULT) if f.user != user.login and user.is_admin0: f.set_password(flask.request.form['password']) # TODO: Add name change but this have to be done with file also ! <04-11-20, Léo Viallon-Galinier> # db.session.commit() return flask.redirect('/d/{}'.format(f.user)) else: return flask.render_template('file_edit.html', user=user, f=f) @app.route('/f//del') @fll.login_required def delfile(id): user = fll.current_user f = File.query.get(id) if f is None: return flask.redirect(URL_DEFAULT) if f.user != user.login and user.is_admin', methods=['GET', 'POST']) @fll.login_required def edituser(user): new = user=='new' user = User.query.get(user) if not new and user is None: return flask.redirect('/admin/users') if fll.current_user.is_admin >= levels_users['Admin users'] and \ (new or fll.current_user.is_admin >= user.is_admin): if flask.request.method=="POST": username = flask.request.form['username'] name = flask.request.form['name'] password = flask.request.form['password'] level = int(flask.request.form['level']) if level not in levels_users.values(): flask.flash('Level unknown') return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users) if fll.current_user.is_admin < levels_users['Admin']: if not level < fll.current_user.is_admin: flask.flash('Invalid level') return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users) if len(password)<2 and new: flask.flash('Mot de passe trop court') return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users) if new and User.query.get(username) is not None: flask.flash('Login déjà utilisé ! Choisissez un autre !') return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users) if new: usertoadd = User(login=username, name=name, is_admin=level) usertoadd.set_password(password) db.session.add(usertoadd) else: user.name = name if len(password)>=2: user.set_password(password) user.is_admin = level db.session.commit() return flask.redirect('/admin/users') else: return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users) return flask.redirect(URL_DEFAULT) @app.route('/admin/users//del') @fll.login_required def deluser(user): user = User.query.get(user) if user is None: return flask.redirect('/admin/users') if fll.current_user.is_admin >= levels_users['Admin'] or \ fll.current_user.is_admin >= levels_users['Admin users'] and \ fll.current_user.is_admin > user.is_admin: confirm = flask.request.args.get('confirm') if confirm is not None: delete_files(user=user) db.session.delete(user) db.session.commit() return flask.redirect('/admin/users') return flask.render_template("admin_user_del.html", user=fll.current_user, useredit=user) @app.route('/password', methods=['GET', 'POST']) @fll.login_required def change_password(): if flask.request.method=="POST": password = flask.request.form['password'] if len(password) < 5: flask.flash('Mot de passe trop court !') else: fll.current_user.set_password(password) db.session.commit() return flask.redirect(URL_DEFAULT) return flask.render_template("password.html", user=fll.current_user) ####################################################################### # Files managament # ####################################################################### def delete_files(user=None): if isinstance(user, str): username = user else: username = user.login lf = File.query.filter_by(user=username).all() for f in lf: fpath = genpath(f.name, f.user) if os.path.isfile(fpath): os.remove(fpath) db.session.delete(f) db.session.commit() if __name__ == '__main__': app.run(debug=True, host='127.0.0.1', port=8080 )