From feffe779d2546afaec71d537ab0b8be6bb46022c Mon Sep 17 00:00:00 2001 From: Burathar Date: Sat, 20 Mar 2021 22:06:43 +0100 Subject: [PATCH] Add login functionality --- biscd/biscd/froms.py | 8 ++-- biscd/biscd/models/__init__.py | 3 -- biscd/biscd/models/project.py | 10 ++-- biscd/biscd/models/user.py | 20 +++++--- biscd/biscd/models/yaml_serializable.py | 60 +++++++++++++----------- biscd/biscd/routes.py | 5 +- biscd/biscd/templates/index.html | 2 +- biscd/biscd/templates/login.html | 23 +++++++++ biscd/biscd/templates/register.html | 15 ++++++ installation-files/projects_example.yaml | 4 +- installation-files/users_example.yaml | 3 +- 11 files changed, 100 insertions(+), 53 deletions(-) create mode 100644 biscd/biscd/templates/login.html create mode 100644 biscd/biscd/templates/register.html diff --git a/biscd/biscd/froms.py b/biscd/biscd/froms.py index 25cd804..cb9f948 100644 --- a/biscd/biscd/froms.py +++ b/biscd/biscd/froms.py @@ -13,19 +13,19 @@ class LoginForm(FlaskForm): class RegistrationForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(min=4, max=64)]) email = StringField('Email', validators=[DataRequired(), Email()]) - password = PasswordField('Password', validators=[DataRequired(), Length(min=10, max=128)]) + password = PasswordField('Password', validators=[DataRequired(), Length(min=4, max=128)]) password2 = PasswordField( 'Repeat Password', validators=[DataRequired(), EqualTo('password')]) submit = SubmitField('Register') def validate_username(self, username): user = User.get(name=username.data) - if not any(user): + if any(user): raise ValidationError('Please use a different username.') def validate_email(self, email): user = User.get(email=email.data) - if not any(user): + if any(user): raise ValidationError('Please use a different email adress.') class NewProjectForm(FlaskForm): @@ -34,5 +34,5 @@ class NewProjectForm(FlaskForm): def validate_projectname(self, projectname): project = Project.get(name=projectname.data) - if not any(project): + if any(project): raise ValidationError('Please use a different projectname.') diff --git a/biscd/biscd/models/__init__.py b/biscd/biscd/models/__init__.py index 5452de6..780340d 100644 --- a/biscd/biscd/models/__init__.py +++ b/biscd/biscd/models/__init__.py @@ -1,6 +1,3 @@ from .project import Project from .user import User from .yaml_serializable import YamlSerializable - -User.initialize() -Project.initialize() diff --git a/biscd/biscd/models/project.py b/biscd/biscd/models/project.py index f45d541..b68f23c 100644 --- a/biscd/biscd/models/project.py +++ b/biscd/biscd/models/project.py @@ -3,13 +3,15 @@ from biscd import config from .yaml_serializable import YamlSerializable class Project(YamlSerializable): - @property - def _storage_file(self): + + #@property + @classmethod + def _storage_file(cls): return Path(config.config_dir()) / 'projects.yaml' - @property + #@property @classmethod - def _yaml_object_name(self): + def _yaml_object_name(cls): return 'projects' def __init__(self, name=None, branche='master'): diff --git a/biscd/biscd/models/user.py b/biscd/biscd/models/user.py index aa751d6..0eec111 100644 --- a/biscd/biscd/models/user.py +++ b/biscd/biscd/models/user.py @@ -1,21 +1,27 @@ from pathlib import Path from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import UserMixin from biscd import config -from .yaml_serializable import YamlSerializable from biscd import login +from .yaml_serializable import YamlSerializable -class User(YamlSerializable): +class User(UserMixin, YamlSerializable): - @property - def _storage_file(self): + #@property + @classmethod + def _storage_file(cls): return Path(config.config_dir()) / 'users.yaml' - @property + #@property @classmethod - def _yaml_object_name(self): + def _yaml_object_name(cls): return 'users' + def get_id(self): + """Override for UserMixin""" + return self.name + def __init__(self, name=None, email=None): self.name = name self.email = email @@ -31,4 +37,4 @@ class User(YamlSerializable): @login.user_loader def load_user(name): - return super.get(name) + return User.first(name=name) diff --git a/biscd/biscd/models/yaml_serializable.py b/biscd/biscd/models/yaml_serializable.py index 5424e16..37f636a 100644 --- a/biscd/biscd/models/yaml_serializable.py +++ b/biscd/biscd/models/yaml_serializable.py @@ -18,13 +18,13 @@ class YamlSerializable(object): required_attributes = ['name'] @classmethod - @property + #@property @abstractmethod def _storage_file(cls): pass @classmethod - @property + #@property @abstractmethod def _yaml_object_name(cls): pass @@ -41,7 +41,7 @@ class YamlSerializable(object): if self.name is None: raise TypeError("Name cannot be None") ymlsls = self._get_all_from_file() - if self.name in ([*ymlsl][0] for ymlsl in ymlsls): + if self.name in [[*ymlsl][0] for ymlsl in ymlsls]: if overwrite: ymlsls[self.name] = self.config_dict else: @@ -53,11 +53,17 @@ class YamlSerializable(object): @classmethod def first_or_404(cls, **kwargs): - ymlsl = next(cls.get(**kwargs), None) + ymlsl = cls.first(**kwargs) if ymlsl is None: abort(404) - else: - return ymlsl + return ymlsl + + @classmethod + def first(cls, **kwargs): + ymlsls = cls.get(**kwargs) + if not any(ymlsls): + return None + return ymlsls[0] @classmethod def get(cls, **kwargs): @@ -73,12 +79,13 @@ class YamlSerializable(object): for key, value in kwargs.items(): # 'name' has to be evaluated separately; 'name' is the key of the entire object if key == 'name': - ymlsl_dicts = (ymlsl_dict for ymlsl_dict in ymlsl_dicts - if [*ymlsl_dict][0] == value) - - # For other keys, filter out any item that does not contain a key, - # or that not match the key's value - ymlsl_dicts = (ymlsl_dict for ymlsl_dict in ymlsl_dicts if ymlsl_dict.key == value) + ymlsl_dicts = [ymlsl_dict for ymlsl_dict in ymlsl_dicts + if [*ymlsl_dict][0] == value] + else: + # For other keys, filter out any item that does not contain a key, + # or that not match the key's value + ymlsl_dicts = [ymlsl_dict for ymlsl_dict in ymlsl_dicts + if ymlsl_dict.get(key, None) == value] # After each iteration: if no item is left, return None if not any(ymlsl_dicts): @@ -89,6 +96,15 @@ class YamlSerializable(object): ymlsls.append(cls._ymlserializable_from_dict(ymlsl_dict)) return ymlsls + @classmethod + def list_names(cls): + ymlsls = cls._get_all_from_file() + ymlserializables_list = [] + for ymlsl in ymlsls: + name = [*ymlsl][0] + ymlserializables_list.append(name) + return ymlserializables_list + @classmethod def _ymlserializable_from_dict(cls, ymldict): if ymldict is None: @@ -110,24 +126,14 @@ class YamlSerializable(object): ymlsl.__dict__ = ymldict return ymlsl - @classmethod - def list(cls): - ymlsls = cls._get_all_from_file() - ymlserializables_list = [] - for ymlsl in ymlsls: - name = [*ymlsl][0] - ymlserializables_list.append(name) - return ymlserializables_list - @classmethod def _get_all_from_file(cls): - with open(cls._storage_file) as file: - ymlsls = yaml.load(file, yaml.FullLoader).get(cls._yaml_object_name) - + with open(cls._storage_file()) as file: + ymlsls = yaml.load(file, yaml.FullLoader).get(cls._yaml_object_name()) return ymlsls @classmethod def _save_all_to_file(cls, ymlsls): - ymlserializables_object = {cls._yaml_object_name : ymlsls} - with open(cls._storage_file, 'w') as file: - yaml.dump(ymlserializables_object, file) \ No newline at end of file + ymlserializables_object = {cls._yaml_object_name() : ymlsls} + with open(cls._storage_file(), 'w') as file: + yaml.dump(ymlserializables_object, file) diff --git a/biscd/biscd/routes.py b/biscd/biscd/routes.py index 4eab840..6fbbe95 100644 --- a/biscd/biscd/routes.py +++ b/biscd/biscd/routes.py @@ -14,7 +14,7 @@ def index(): project = Project.first_or_404(name=form.projectname.data) project.save() flash('You added a project!') - project_names = Project.list() + project_names = Project.list_names() return render_template('index.html', form=form, project_names=project_names) @app.route('/login', methods=['GET', 'POST']) @@ -23,7 +23,7 @@ def login(): return redirect(url_for('index')) form = LoginForm() if form.validate_on_submit(): - user = User.first_or_404(name=form.username.data) + user = User.first(name=form.username.data) if user is None or not user.check_password(form.password.data): flash('Invalid username or password') return redirect(url_for('login')) @@ -34,6 +34,7 @@ def login(): return redirect(next_page) return render_template('login.html', title='Sign In', form=form) +@login_required @app.route('/logout') def logout(): logout_user() diff --git a/biscd/biscd/templates/index.html b/biscd/biscd/templates/index.html index b7be233..f2d3ff1 100644 --- a/biscd/biscd/templates/index.html +++ b/biscd/biscd/templates/index.html @@ -25,7 +25,7 @@
- {{ wtf.quick_form(form) }} + {{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
{% endblock %} diff --git a/biscd/biscd/templates/login.html b/biscd/biscd/templates/login.html new file mode 100644 index 0000000..47905f8 --- /dev/null +++ b/biscd/biscd/templates/login.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} + +
+
+
+

Sign In

+
+
+ {{ form.hidden_tag() }} + {{ wtf.form_field(form.username) }} + {{ wtf.form_field(form.password) }} + {{ wtf.form_field(form.remember_me) }} + {{ wtf.form_field(form.submit, class='btn btn-primary') }} +
+
+

New User? Click to Register!

+
+
+
+{% endblock %} diff --git a/biscd/biscd/templates/register.html b/biscd/biscd/templates/register.html new file mode 100644 index 0000000..7fb3d5c --- /dev/null +++ b/biscd/biscd/templates/register.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} +
+
+
+

Register

+ {{ wtf.quick_form(form, button_map={'submit': 'primary'}) }} +
+

Already have an account? Click to Sign In!

+
+
+
+{% endblock %} diff --git a/installation-files/projects_example.yaml b/installation-files/projects_example.yaml index d1bae11..c450106 100644 --- a/installation-files/projects_example.yaml +++ b/installation-files/projects_example.yaml @@ -12,6 +12,4 @@ projects: requirements_file: requirements.txt secret: thisissecret tests: tests.py - url: bar -- Test: - branche: master + url: bar \ No newline at end of file diff --git a/installation-files/users_example.yaml b/installation-files/users_example.yaml index f6e80ca..7d3fc68 100644 --- a/installation-files/users_example.yaml +++ b/installation-files/users_example.yaml @@ -1,5 +1,4 @@ users: - henk: email: mymail@mail.com - password: hash - \ No newline at end of file + password_hash: pbkdf2:sha256:150000$uuFRyvLs$ee9863f169db786e82b9e2abe0c2cf3434e925479d0919f3b4046ebbfa0aeb28 \ No newline at end of file