Skip to content

Commit

Permalink
Авторизация
Browse files Browse the repository at this point in the history
  • Loading branch information
Shindler7 committed May 24, 2020
1 parent 4e78edb commit 5cd9c48
Show file tree
Hide file tree
Showing 15 changed files with 368 additions and 40 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

* проект открыт для редактирования и доработки;
* создан в 2020 году в рамках образовательной программы "Яндекс.Практикум";
* Python 3.8
* Python 3.8.

### Использованные ресурсы ###

* pip install -r requirements.txt (установит все необходимые библиотеки).

### Задачи развития ###

1. С помощью cookies приложение должно запоминать последний набор пользователя и восстанавливать его при возвращении.
2. Предусмотреть авторизацию или иной механизм для редактирования хранящихся данных.
3. Теневая проверка ссылок и удаление некорректных и неработающих (без участия пользователя, в тени).
1. Создать механизм редактирования сведений в базе данных.
2. Теневая проверка ссылок и удаление некорректных и неработающих (без участия пользователя, в тени).
12 changes: 9 additions & 3 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from flask import Flask
from config import Config
from flask_sslify import SSLify
from flask_sqlalchemy import SQLAlchemy as SQLA
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy as SQLA
from flask_sslify import SSLify

from config import Config

# *** временно выключено ***
# import logging
Expand All @@ -21,6 +23,10 @@
db_lib = SQLA(application)
migrate = Migrate(application, db_lib)

# Login
login_manager = LoginManager(application)
login_manager.login_view = 'login'


from app import routes

Expand Down
11 changes: 11 additions & 0 deletions app/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
Система авторизации пользователей.
"""

from app import login_manager
from app.models import User


@login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
5 changes: 3 additions & 2 deletions app/dbpanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

# https://www.tutorialspoint.com/sqlalchemy/sqlalchemy_orm_filter_operators.htm

from app import db_lib
from app.models import Content, Types, Category
from sqlalchemy.exc import SQLAlchemyError

from app import db_lib
from app.exc import *
from app.models import Category, Content, Types


def add_to_db(*, create_types: bool = True, create_category: bool = True, **kwargs):
Expand Down
43 changes: 43 additions & 0 deletions app/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
Формы приложения.
"""

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, validators
from wtforms.validators import DataRequired, EqualTo, ValidationError
from app.models import User


class LoginForm(FlaskForm):
"""
Форма авторизации.
"""
nickname = StringField('Никнейм', validators=[DataRequired()])
password = PasswordField('Пароль', validators=[DataRequired()])
remember_me = BooleanField('Запомнить меня')
submit = SubmitField('Войти')

def validate_nickname(self, nickname):
user = User.query.filter_by(nickname=nickname.data).first()
if user and not user.active:
raise ValidationError('Учётная запись неактивна.')


class RegForm(FlaskForm):
"""
Форма регистрации пользователя.
"""
nickname = StringField('Никнейм', validators=[DataRequired()])
password1 = PasswordField('Пароль', [
validators.DataRequired(),
validators.EqualTo('password2', message='Пароли должны совпадать!')
])
password2 = PasswordField('Пароль повторно', [
validators.DataRequired()
])
submit = SubmitField('Зарегистрировать')

def validate_nickname(self, nickname):
user = User.query.filter_by(nickname=nickname.data).first()
if user is not None:
raise ValidationError('Ник занят. Выберите другой, пожалуйста.')
4 changes: 3 additions & 1 deletion app/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
Принимает через функцию start_module запрос пользователя (user_req).
Возвращает подготовленный для публикации текст.
"""
from __future__ import annotations

import re

from app.dbpanel import DBWork, get_content


Expand Down Expand Up @@ -40,7 +42,7 @@ def get_key_from_request(user_req: str) -> dict:
point_in_phrase = len(user_phrase) - 1

# Кроличья нора: вывести всё сразу.
if user_phrase[0] in ['всё', 'все', 'all'] or not user_phrase:
if not user_phrase or user_phrase[0] in ['всё', 'все', 'all']:
return key_dict

dbw = DBWork()
Expand Down
45 changes: 44 additions & 1 deletion app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,52 @@
Конфигурационные модели для SQL.
"""

from app import db_lib
from datetime import datetime

from sqlalchemy.orm import relationship
from werkzeug.security import check_password_hash, generate_password_hash
from flask_login import UserMixin
from app import login_manager

from app import db_lib


class User(UserMixin, db_lib.Model):
"""
Таблица User - информация о зарегистрированных пользователях.
"""

__tablename__ = 'User'

id = db_lib.Column(db_lib.Integer, primary_key=True)
social_id = db_lib.Column(db_lib.String(64), nullable=True, unique=True)
nickname = db_lib.Column(db_lib.String(64), nullable=False, unique=True)
password_hash = db_lib.Column(db_lib.String(128))
isadmin = db_lib.Column(db_lib.Boolean, default=False)
active = db_lib.Column(db_lib.Boolean, default=True)

def set_password(self, password: str) -> None:
"""
Создание хеш-слепка пароля.
"""
self.password_hash = generate_password_hash(password)

def check_password(self, password: str) -> bool:
"""
Проверка соответствия хэш-слепка пароля введённому паролю.
"""
return check_password_hash(self.password_hash, password)

def __repr__(self):
return f'<User: nickname={self.nickname}, isadmin={self.isadmin}>'

def __str__(self):
return self.nickname


@login_manager.user_loader
def load_user(id):
return User.query.get(int(id))


class Category(db_lib.Model):
Expand Down
131 changes: 104 additions & 27 deletions app/routes.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,127 @@
#!/usr/bin/env python3

from flask import render_template, redirect, request
from flask import flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required, login_user
from werkzeug.urls import url_parse

from app import application
from app.generator import start_module
from app.dbpanel import DBWork
from app.forms import LoginForm, RegForm
from app.generator import start_module
from app.models import User

active_output: str = ''
from app import db_lib

# Дополнительные настройки
application.add_template_global(DBWork().get_list_category, 'list_category')

@application.route('/', methods=['GET', 'POST'])
@application.route('/index', methods=['GET', 'POST'])
def index():
"""
Вывод главной страницы проекта.
"""
global active_output
dbw = DBWork()
output: str = ''
tag_links: list = list()
form_login = LoginForm()
data_render = dict(
title='online',
output='',
category_links=dbw.get_list_category, # chat_string
tag_links=list(), # tag_string
exoutput='',
form_login=form_login
)

# Обработчик запроса пользователя GET
if request.method == 'GET':
chatstring = request.args.get(key="chatstring", default="")
tagstring = request.args.get(key="tagstring", default="")
question_to_base = f'{chatstring} {tagstring}'
data_render['output'] = start_module(question_to_base)

if request.args.get(key='chatstring'):
output = start_module(request.args['chatstring'])
active_output = request.args['chatstring']
data_render['exoutput'] = request.args.get(key='chatstring', default='')

elif request.args.get(key='tagstring'):
output = start_module(f"{active_output} {request.args['tagstring']}")
else:
output = start_module('all')
active_output = ''
tag_links = dbw.get_choices_types(category=data_render['exoutput'])
data_render['tag_links'] = tag_links if len(tag_links) > 1 else list()

tag_links = dbw.get_choices_types(category=active_output)
if len(tag_links) < 2:
tag_links = list()
if request.method == 'POST':
if form_login.validate_on_submit() and form_login.submit.data:
user = User.query.filter_by(nickname=form_login.nickname.data).first()
if user is None or not user.check_password(form_login.password.data):
flash('Неверные данные.', category='error')
return render_template('index.html', **data_render)

login_user(user, remember=True)

if request.method == 'GET':
pass

return render_template('index.html',
title='online',
output=output,
exoutput=active_output,
category_links=dbw.get_list_category,
tag_links=tag_links)
return render_template('index.html', **data_render)


@application.route('/db')
def createbase():
pass
@login_required
def db_view():
"""
Для авторизованных пользователей работа с записями в базе данных.
:return: HttpResponse, форма.
"""

if not current_user.isadmin:
return f'Техническая страница недоступна для вашей учётной записи. <a href={url_for("index")}>На главную</a>.'

return 'В стадии разработки.'


@application.route('/login', methods=['GET', 'POST'])
@application.template_global()
def login():
"""
Форма регистрации и авторизации пользователя.
:return: HttpResponse, форма.
"""
# https://habr.com/ru/post/346346/

if current_user.is_authenticated:
return redirect(url_for('index'))

form_login = LoginForm(prefix='login')

if form_login.validate_on_submit():
user = User.query.filter_by(nickname=form_login.nickname.data).first()
if user is None or not user.check_password(form_login.password.data):
flash('Неверные данные.', category='error')
return redirect(url_for('login'))
login_user(user, remember=True)

next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('index')
return redirect(next_page)

return render_template('auth/login.html', title='Авторизация',
form=form_login)

@application.route('/singin', methods=['GET', 'POST'])
@application.template_global()
def singin():

if current_user.is_authenticated:
return redirect(url_for('index'))

form_reg = RegForm()

if form_reg.validate_on_submit():
user = User(
nickname=form_reg.nickname.data
)
user.set_password(form_reg.password1.data)
db_lib.session.add(user)
db_lib.session.commit()

login_user(user, remember=True)

return redirect(url_for('index'))

return render_template('auth/registration.html', title='Регистрация',
form_reg=form_reg)
38 changes: 38 additions & 0 deletions app/templates/auth/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% block content %}

<h1>Портал в закулисье</h1>

{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}

<form method="POST" novalidate>
{{ form.hidden_tag() }}

<p>
{{ form.nickname.label }}*<br>
{{ form.nickname(size=32) }}<br>
{% for error in form.nickname.errors %}
<span style="color: #ff0000;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}*<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.submit() }}
</p>
</form>

{% endblock %}
Loading

0 comments on commit 5cd9c48

Please sign in to comment.