Browse Source

Add login functionality

master
Burathar 4 years ago
parent
commit
feffe779d2
  1. 8
      biscd/biscd/froms.py
  2. 3
      biscd/biscd/models/__init__.py
  3. 10
      biscd/biscd/models/project.py
  4. 20
      biscd/biscd/models/user.py
  5. 60
      biscd/biscd/models/yaml_serializable.py
  6. 5
      biscd/biscd/routes.py
  7. 2
      biscd/biscd/templates/index.html
  8. 23
      biscd/biscd/templates/login.html
  9. 15
      biscd/biscd/templates/register.html
  10. 4
      installation-files/projects_example.yaml
  11. 3
      installation-files/users_example.yaml

8
biscd/biscd/froms.py

@ -13,19 +13,19 @@ class LoginForm(FlaskForm): @@ -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): @@ -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.')

3
biscd/biscd/models/__init__.py

@ -1,6 +1,3 @@ @@ -1,6 +1,3 @@
from .project import Project
from .user import User
from .yaml_serializable import YamlSerializable
User.initialize()
Project.initialize()

10
biscd/biscd/models/project.py

@ -3,13 +3,15 @@ from biscd import config @@ -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'):

20
biscd/biscd/models/user.py

@ -1,21 +1,27 @@ @@ -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): @@ -31,4 +37,4 @@ class User(YamlSerializable):
@login.user_loader
def load_user(name):
return super.get(name)
return User.first(name=name)

60
biscd/biscd/models/yaml_serializable.py

@ -18,13 +18,13 @@ class YamlSerializable(object): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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)
ymlserializables_object = {cls._yaml_object_name() : ymlsls}
with open(cls._storage_file(), 'w') as file:
yaml.dump(ymlserializables_object, file)

5
biscd/biscd/routes.py

@ -14,7 +14,7 @@ def index(): @@ -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(): @@ -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(): @@ -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()

2
biscd/biscd/templates/index.html

@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
{{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
</div>
</div>
{% endblock %}

23
biscd/biscd/templates/login.html

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<div class="row">
<div class="col-xs-0 col-md-1"></div>
<div class="col-xs-8 col-md-4">
<h1>Sign In</h1>
<br>
<form action="" method="post" class="form" role="form">
{{ 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') }}
</form>
<br>
<p>New User? <a href="{{ url_for('register') }}">Click to Register!</a></p>
</div>
<div class="col-xs-0 col-md-7"></div>
</div>
{% endblock %}

15
biscd/biscd/templates/register.html

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<div class="row">
<div class="col-xs-0 col-md-1"></div>
<div class="col-xs-8 col-md-4">
<h1>Register</h1>
{{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
<br>
<p>Already have an account? <a href="{{ url_for('login') }}">Click to Sign In!</a></p>
</div>
<div class="col-xs-0 col-md-7"></div>
</div>
{% endblock %}

4
installation-files/projects_example.yaml

@ -12,6 +12,4 @@ projects: @@ -12,6 +12,4 @@ projects:
requirements_file: requirements.txt
secret: thisissecret
tests: tests.py
url: bar
- Test:
branche: master
url: bar

3
installation-files/users_example.yaml

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
users:
- henk:
email: mymail@mail.com
password: hash
password_hash: pbkdf2:sha256:150000$uuFRyvLs$ee9863f169db786e82b9e2abe0c2cf3434e925479d0919f3b4046ebbfa0aeb28
Loading…
Cancel
Save