diff --git a/forms.py b/forms.py index 89d8675..072c8d6 100644 --- a/forms.py +++ b/forms.py @@ -1,5 +1,5 @@ from flask_wtf import FlaskForm -from wtforms import StringField, SubmitField, TextAreaField +from wtforms import StringField, SubmitField, TextAreaField, BooleanField, PasswordField from wtforms.validators import DataRequired, Email @@ -8,3 +8,11 @@ class ContactForm(FlaskForm): email = StringField("Email: ", validators=[Email()]) message = TextAreaField("Message", validators=[DataRequired()]) submit = SubmitField("Submit") + + +class LoginForm(FlaskForm): + username = StringField("Username", validators=[DataRequired()]) + password = PasswordField("Password", validators=[DataRequired()]) + remember = BooleanField("Remember Me") + submit = SubmitField() + diff --git a/main.py b/main.py index 33d3e9f..9b1b5c1 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,14 @@ import flask from flask import Flask, request, current_app, url_for, render_template, flash, redirect +from werkzeug.security import generate_password_hash, check_password_hash from flask_script import Manager, Shell from flask_migrate import Migrate, MigrateCommand +from flask_login import LoginManager, UserMixin, login_required, login_user, current_user, logout_user from jinja2 import Template from flask_sqlalchemy import SQLAlchemy from datetime import datetime -from forms import ContactForm +from forms import ContactForm, LoginForm app = Flask(__name__) app.debug = True @@ -14,6 +16,8 @@ app.config['SECRET_KEY'] = '0d6e368e-bd0c-11ea-921d-9342d47f60ca' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///db.sqlite" db = SQLAlchemy(app) +login_manager = LoginManager(app) +login_manager.login_view = 'login' migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) @@ -75,6 +79,37 @@ class Feedback(db.Model): return "<{}:{}>".format(self.id, self.name) +class User(db.Model, UserMixin): + __tablename__ = 'users' + id = db.Column(db.Integer(), primary_key=True) + name = db.Column(db.String(100)) + username = db.Column(db.String(50), nullable=False, unique=True) + email = db.Column(db.String(100), nullable=False, unique=True) + password_hash = db.Column(db.String(100), nullable=False) + created_on = db.Column(db.DateTime(), default=datetime.utcnow) + updated_on = db.Column(db.DateTime(), default=datetime.utcnow, onupdate=datetime.utcnow) + + def __repr__(self): + return "<{}:{}>".format(self.id, self.username) + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + + +@login_manager.user_loader +def load_user(user_id): + return db.session.query(User).get(user_id) + + +@app.route('/admin/') +@login_required +def admin(): + return render_template('admin.html') + + @manager.command def faker(): print("Команда для добавления поддельных данных в таблицы") @@ -87,20 +122,26 @@ def index(): @app.route('/login/', methods=['post', 'get']) def login(): - username = '' - password = '' - message = '' - if request.method == 'POST': - username = request.form.get('username') # запрос к данным формы - password = request.form.get('password') + if current_user.is_authenticated: + return redirect(url_for('admin')) + form = LoginForm() + if form.validate_on_submit(): + user = db.session.query(User).filter(User.username == form.username.data).first() + if user and user.check_password(form.password.data): + login_user(user, remember=form.remember.data) + nextpage = request.args.get('next', url_for('admin')) + return redirect(nextpage) + else: + flash("Invalid username/password", 'error') + return render_template('login.html', form=form) - if username == 'root' and password == 'pass': - message = "Correct username and password" - else: - message = "Wrong username or password" - - return render_template('login.html', message=message) +@app.route('/logout/') +@login_required +def logout(): + logout_user() + flash("You have been logged out.") + return redirect(url_for('login')) @app.route('/contact/', methods=['get', 'post']) def contact(): diff --git a/migrations/versions/d0915c4b79cd_add_users_table.py b/migrations/versions/d0915c4b79cd_add_users_table.py new file mode 100644 index 0000000..f9ee485 --- /dev/null +++ b/migrations/versions/d0915c4b79cd_add_users_table.py @@ -0,0 +1,39 @@ +"""add users table + +Revision ID: d0915c4b79cd +Revises: 60a6132e925b +Create Date: 2020-07-03 14:14:52.140939 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd0915c4b79cd' +down_revision = '60a6132e925b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=True), + sa.Column('username', sa.String(length=50), nullable=False), + sa.Column('email', sa.String(length=100), nullable=False), + sa.Column('password_hash', sa.String(length=100), nullable=False), + sa.Column('created_on', sa.DateTime(), nullable=True), + sa.Column('updated_on', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email'), + sa.UniqueConstraint('username') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index a49ff12..8550b66 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ Flask-Script==2.0.6 email_validator==1.1.1 Flask_SQLAlchemy==2.4.3 SQLAlchemy==1.3.18 -flask-migrate==2.5.3 \ No newline at end of file +flask-migrate==2.5.3 +flask_login==0.5.0 diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..2493e10 --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,21 @@ + + + + + Title + + + +

Logged in User details

+ + + +

Logout

+ + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index aa94936..c8da230 100644 --- a/templates/login.html +++ b/templates/login.html @@ -6,21 +6,36 @@ - {% if message %} -

{{ message }}

- {% endif %} + {% for category, message in get_flashed_messages(with_categories=true) %} + {{ message }} + {% endfor %}
-

- - + {{ form.csrf_token }} +

+ {{ form.username.label() }} + {{ form.username() }} + {% if form.username.errors %} + {% for error in form.username.errors %} + {{ error }} + {% endfor %} + {% endif %}

- - + {{ form.password.label() }} + {{ form.password() }} + {% if form.password.errors %} + {% for error in form.password.errors %} + {{ error }} + {% endfor %} + {% endif %}

- + {{ form.remember.label() }} + {{ form.remember() }} +

+

+ {{ form.submit() }}