Update login
Add profile
This commit is contained in:
@@ -26,9 +26,31 @@ formatter = logging.Formatter(app.config['LONG_LOG_FORMAT'])
|
|||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
app.logger.addHandler(handler)
|
app.logger.addHandler(handler)
|
||||||
|
|
||||||
|
from . import lib, models
|
||||||
|
|
||||||
|
@app.context_processor
|
||||||
|
def inject_data():
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
result['user'] = None
|
||||||
|
if lib.get_user():
|
||||||
|
result['user'] = lib.get_user()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@app.teardown_appcontext
|
||||||
|
def shutdown_session(exception=None):
|
||||||
|
models.db_session.close_all()
|
||||||
|
|
||||||
# API
|
# API
|
||||||
from . import ns_api
|
from . import ns_api
|
||||||
|
|
||||||
|
# Авторизация
|
||||||
|
from . import ns_login
|
||||||
|
|
||||||
|
# Профиль
|
||||||
|
from . import ns_profile
|
||||||
|
|
||||||
# Пользователи
|
# Пользователи
|
||||||
from . import ns_user
|
from . import ns_user
|
||||||
|
|
||||||
|
|||||||
36
myapp/decorators.py
Normal file
36
myapp/decorators.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'http://remizoffalex.ru'
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from flask import session, redirect
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
def login_required(func):
|
||||||
|
@wraps(func)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if 'logged_in' in session and 'user_id' in session:
|
||||||
|
user = models.db_session.query(
|
||||||
|
models.User
|
||||||
|
).filter(
|
||||||
|
models.User.id==session['user_id']
|
||||||
|
).first()
|
||||||
|
if user:
|
||||||
|
user.last_activity = datetime.datetime.now()
|
||||||
|
models.db_session.commit()
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
session.pop('logged_in', None)
|
||||||
|
session.pop('user_id', None)
|
||||||
|
return redirect('/login')
|
||||||
|
else:
|
||||||
|
return redirect('/login')
|
||||||
|
return decorated_function
|
||||||
@@ -7,6 +7,6 @@ __url__ = 'https://remizoffalex.ru'
|
|||||||
from .pagination import Pagination, getpage
|
from .pagination import Pagination, getpage
|
||||||
from .passwd import pwgen, get_hash_password
|
from .passwd import pwgen, get_hash_password
|
||||||
from .storage import gettree, gethashtree
|
from .storage import gettree, gethashtree
|
||||||
from .info import get_user, get_ip
|
from .info import get_user
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
|
|||||||
@@ -23,14 +23,3 @@ def get_user():
|
|||||||
session.pop('user_id', None)
|
session.pop('user_id', None)
|
||||||
return None
|
return None
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_ip():
|
|
||||||
"""
|
|
||||||
Получить IP
|
|
||||||
"""
|
|
||||||
result = ''
|
|
||||||
if request.headers.getlist("X-Forwarded-For"):
|
|
||||||
result = request.headers.get("X-Forwarded-For").split(",")[0]
|
|
||||||
else:
|
|
||||||
result = request.remote_addr
|
|
||||||
return result
|
|
||||||
|
|||||||
@@ -1,35 +1,32 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: UTF-8 -*-
|
|
||||||
|
|
||||||
__author__ = 'RemiZOffAlex'
|
__author__ = 'RemiZOffAlex'
|
||||||
__copyright__ = '(c) RemiZOffAlex'
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__email__ = 'remizoffalex@mail.ru'
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'https://remizoffalex.ru'
|
||||||
|
|
||||||
|
import uuid
|
||||||
import random
|
import random
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from .. import app
|
|
||||||
|
|
||||||
def pwgen(length=15, hex=False):
|
def pwgen(length=15, hex=False):
|
||||||
"""
|
"""
|
||||||
Генератор пароля
|
Генератор пароля
|
||||||
"""
|
"""
|
||||||
keylist='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
keylist = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
if hex:
|
if hex:
|
||||||
keylist='0123456789ABCDEF'
|
keylist = '0123456789ABCDEF'
|
||||||
password=[]
|
password = []
|
||||||
|
|
||||||
while len(password) < length:
|
while len(password) < length:
|
||||||
a_char = random.choice(keylist)
|
a_char = random.choice(keylist)
|
||||||
password.append(a_char)
|
password.append(a_char)
|
||||||
return ''.join(password)
|
return ''.join(password)
|
||||||
|
|
||||||
def get_hash_password(password, salt = None):
|
def get_hash_password(password, salt=None):
|
||||||
"""
|
"""
|
||||||
Получить хеш пароля SHA-512
|
Получить хеш пароля SHA-512
|
||||||
"""
|
"""
|
||||||
if salt == None:
|
if salt is None:
|
||||||
salt = uuid.uuid4().hex
|
salt = uuid.uuid4().hex
|
||||||
text = password.encode('utf-8') + salt.encode('utf-8')
|
text = password.encode('utf-8') + salt.encode('utf-8')
|
||||||
h = hashlib.sha512()
|
h = hashlib.sha512()
|
||||||
|
|||||||
18
myapp/ns_login/__init__.py
Normal file
18
myapp/ns_login/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'https://remizoffalex.ru'
|
||||||
|
|
||||||
|
import os
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
from .. import app
|
||||||
|
|
||||||
|
|
||||||
|
my_loader = jinja2.ChoiceLoader([
|
||||||
|
app.jinja_loader,
|
||||||
|
jinja2.FileSystemLoader(os.path.dirname(os.path.abspath(__file__)) + "/templates"),
|
||||||
|
])
|
||||||
|
app.jinja_loader = my_loader
|
||||||
17
myapp/ns_login/forms.py
Normal file
17
myapp/ns_login/forms.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'http://remizoffalex.ru'
|
||||||
|
|
||||||
|
from wtforms import (
|
||||||
|
validators,
|
||||||
|
Form,
|
||||||
|
StringField,
|
||||||
|
PasswordField
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(Form):
|
||||||
|
username = StringField('Логин', [validators.Length(min=4, max=25)])
|
||||||
|
password = PasswordField('Пароль', [validators.DataRequired()])
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<div class="col-md-6 ml-auto mr-auto">
|
<div class="col-md-6 ml-auto mr-auto">
|
||||||
<div class="card">
|
<h3>Логин</h3>
|
||||||
<div class="card-header">Логин</div>
|
<hr />
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
<form class="form-horizontal" action="/login" method="post" autocomplete="on">
|
<form class="form-horizontal" action="/login" method="post" autocomplete="on">
|
||||||
|
|
||||||
<div style="margin-bottom: 25px" class="input-group">
|
<div style="margin-bottom: 25px" class="input-group">
|
||||||
@@ -25,7 +24,5 @@
|
|||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
45
myapp/ns_login/views.py
Normal file
45
myapp/ns_login/views.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'https://remizoffalex.ru'
|
||||||
|
|
||||||
|
from flask import render_template, escape, request, session, redirect
|
||||||
|
|
||||||
|
from .. import app, lib, models
|
||||||
|
from . import forms
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
"""
|
||||||
|
Логин
|
||||||
|
"""
|
||||||
|
pagedata = {}
|
||||||
|
pagedata['form'] = forms.LoginForm(request.form)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if pagedata['form'].validate():
|
||||||
|
user = models.db_session.query(
|
||||||
|
models.User
|
||||||
|
).filter(
|
||||||
|
models.User.name == escape(pagedata['form'].username.data),
|
||||||
|
models.User.password == lib.get_hash_password(
|
||||||
|
escape(pagedata['form'].password.data),
|
||||||
|
app.config['SECRET_KEY']
|
||||||
|
),
|
||||||
|
models.User.disabled == False
|
||||||
|
).first()
|
||||||
|
if user:
|
||||||
|
session['logged_in'] = True
|
||||||
|
session['user_id'] = user.id
|
||||||
|
return redirect('/', code=302)
|
||||||
|
|
||||||
|
body = render_template('login.html', pagedata=pagedata)
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
session.pop('logged_in', None)
|
||||||
|
session.pop('user_id', None)
|
||||||
|
return redirect("/", code=302)
|
||||||
18
myapp/ns_profile/__init__.py
Normal file
18
myapp/ns_profile/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'http://remizoffalex.ru'
|
||||||
|
|
||||||
|
import os
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
from .. import app
|
||||||
|
|
||||||
|
|
||||||
|
my_loader = jinja2.ChoiceLoader([
|
||||||
|
app.jinja_loader,
|
||||||
|
jinja2.FileSystemLoader(os.path.dirname(os.path.abspath(__file__)) + "/templates"),
|
||||||
|
])
|
||||||
|
app.jinja_loader = my_loader
|
||||||
9
myapp/ns_profile/templates/profile.html
Normal file
9
myapp/ns_profile/templates/profile.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends "skeleton.html" %}
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<h3>Профиль</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<p>Зарегистрирован: {{ user.created.strftime('%Y-%m-%d') }}</p>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
22
myapp/ns_profile/views.py
Normal file
22
myapp/ns_profile/views.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
__author__ = 'RemiZOffAlex'
|
||||||
|
__copyright__ = '(c) RemiZOffAlex'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
|
__url__ = 'http://remizoffalex.ru'
|
||||||
|
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
|
from .. import app
|
||||||
|
from ..decorators import login_required
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/profile')
|
||||||
|
@login_required
|
||||||
|
def profile():
|
||||||
|
"""
|
||||||
|
Личный профиль пользователя
|
||||||
|
"""
|
||||||
|
pagedata = {}
|
||||||
|
pagedata['title'] = 'Мой профиль - {}'.format(app.config['TITLE'])
|
||||||
|
body = render_template('profile.html', pagedata=pagedata)
|
||||||
|
return body
|
||||||
@@ -13,14 +13,28 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{{ pagedata['form'].text.label }}
|
{{ pagedata['form'].text.label }}
|
||||||
{{ pagedata['form'].text(class="form-control") }}
|
{{ pagedata['form'].text(class="form-control") }}
|
||||||
<script>
|
|
||||||
CKEDITOR.replace( 'text', {
|
|
||||||
customConfig: '/static/js/ckeditor-conf.js'
|
|
||||||
} );
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-outline-success float-right"><i class="fa fa-save-o"></i> Сохранить</button>
|
<button class="btn btn-outline-success float-right"><i class="fa fa-save-o"></i> Сохранить</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
CKEDITOR.replace( 'text', {
|
||||||
|
customConfig: '/static/js/ckeditor-conf.js'
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
<div class="container-fluid py-2">
|
<div class="container-fluid py-2">
|
||||||
|
|
||||||
|
{% if session.logged_in %}
|
||||||
|
<div class="btn-group float-right">
|
||||||
|
<a class="btn btn-outline-secondary border-0" href="/profile"><i class="fa fa-user"></i></a>
|
||||||
|
<a class="btn btn-outline-danger border-0" href="/logout"><i class="fa fa-sign-out"></i></a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
<a class="btn btn-outline-success border-0 float-right" href="/login"><i class="fa fa-sign-in"></i></a>
|
<a class="btn btn-outline-success border-0 float-right" href="/login"><i class="fa fa-sign-in"></i></a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
|
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/edit"><i class="fa fa-edit"></i> Редактор</a>
|
<a class="btn btn-outline-secondary border-0" href="/edit"><i class="fa fa-edit"></i> Редактор</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/users">Vue & axios</a>
|
<a class="btn btn-outline-secondary border-0" href="/users">Vue & axios</a>
|
||||||
|
|||||||
@@ -5,4 +5,4 @@
|
|||||||
|
|
||||||
{{ pagedata['text']|safe }}
|
{{ pagedata['text']|safe }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock body %}
|
||||||
|
|||||||
@@ -8,12 +8,21 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% endblock %}
|
{% endblock body %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
{% endblock %}
|
<script type="text/javascript">
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock script %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -39,14 +39,3 @@ def edit():
|
|||||||
return body
|
return body
|
||||||
body = render_template('edit.html', pagedata=pagedata)
|
body = render_template('edit.html', pagedata=pagedata)
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
|
||||||
def login():
|
|
||||||
"""
|
|
||||||
Логин
|
|
||||||
"""
|
|
||||||
pagedata = {}
|
|
||||||
pagedata['form'] = forms.LoginForm(request.form)
|
|
||||||
body = render_template('login.html', pagedata=pagedata)
|
|
||||||
return body
|
|
||||||
|
|||||||
Reference in New Issue
Block a user