-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth.py
305 lines (232 loc) · 11.1 KB
/
auth.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
from app import db
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.urls import url_parse
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from flask_wtf import FlaskForm
from wtforms import TextField, StringField, PasswordField, BooleanField, validators, SubmitField, SelectField
from wtforms.validators import *
from flask_table import Table, BoolCol, Col, ButtonCol
from languages import language_choices
from flask_babelex import gettext, lazy_gettext
from flask_babelex import refresh as babrefresh
from sqlalchemy_utils import UUIDType
import uuid
from flask_user import UserManager, UserMixin, current_user, login_required, roles_required
class User(db.Model, UserMixin):
__tablename__ = "users"
id = db.Column(UUIDType, primary_key = True, default=uuid.uuid4, nullable=False)
username = db.Column(db.String(80), unique = True, nullable = False )
password = db.Column(db.String, nullable = False)
locale = db.Column(db.String, default= 'fr', nullable = False)
active = db.Column('is_active', db.Boolean, nullable=False, default=True)
first_name = db.Column(db.String(50), nullable=False, default='')
last_name = db.Column(db.String(50), nullable=False, default='')
# Define the relationship to Role via UserRoles
roles = db.relationship('Role', secondary='user_roles')
def set_password(self, password):
self.password = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password, password)
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(50), unique = True)
description = db.Column(db.String(180))
class UserRoles(db.Model):
__tablename__ = 'user_roles'
id = db.Column(db.Integer, primary_key = True)
user_id = db.Column(UUIDType, db.ForeignKey('users.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
bp = Blueprint('auth', __name__, url_prefix='/auth')
class UUIDLoginForm(FlaskForm):
UUID = TextField('UUID',[validators.DataRequired()])
remember_me = BooleanField(lazy_gettext(u'Remember me'))
submit = SubmitField(lazy_gettext(u'Login'))
class CustomUserManager(UserManager):
def login_view(self):
"""Prepare and process the login form."""
# Authenticate username/email and login authenticated users.
safe_next_url = self._get_safe_next_url('next', self.USER_AFTER_LOGIN_ENDPOINT)
safe_reg_next = self._get_safe_next_url('reg_next', self.USER_AFTER_REGISTER_ENDPOINT)
# Immediately redirect already logged in users
if self.call_or_get(current_user.is_authenticated) and self.USER_AUTO_LOGIN_AT_LOGIN:
return redirect(safe_next_url)
# Initialize form
login_form = self.LoginFormClass(request.form) # for login.html
uuidlogin_form = UUIDLoginForm()
register_form = self.RegisterFormClass() # for login_or_register.html
if request.method != 'POST':
login_form.next.data = register_form.next.data = safe_next_url
login_form.reg_next.data = register_form.reg_next.data = safe_reg_next
# Process valid POST
if request.method == 'POST' and login_form.validate():
# Retrieve User
user = None
user_email = None
if self.USER_ENABLE_USERNAME:
# Find user record by username
user = self.db_manager.find_user_by_username(login_form.username.data)
# Find user record by email (with form.username)
if not user and self.USER_ENABLE_EMAIL:
user, user_email = self.db_manager.get_user_and_user_email_by_email(login_form.username.data)
else:
# Find user by email (with form.email)
user, user_email = self.db_manager.get_user_and_user_email_by_email(login_form.email.data)
if user:
# Log user in
safe_next_url = self.make_safe_url(login_form.next.data)
return self._do_login_user(user, safe_next_url, login_form.remember_me.data)
elif request.method == 'POST' and uuidlogin_form.validate():
# Retrieve User
user = None
user_email = None
user = self.db_manager.get_user_by_id(uuidlogin_form.UUID.data)
if user:
# Log user in
safe_next_url = self.make_safe_url(login_form.next.data)
return self._do_login_user(user, safe_next_url, uuidlogin_form.remember_me.data)
# Render form
self.prepare_domain_translations()
template_filename = self.USER_LOGIN_AUTH0_TEMPLATE if self.USER_ENABLE_AUTH0 else self.USER_LOGIN_TEMPLATE
return render_template(template_filename,
form=login_form,
login_form=login_form,
uuidlogin_form=uuidlogin_form,
register_form=register_form)
class UserEditForm(FlaskForm):
password = PasswordField(lazy_gettext(u'Password'),[validators.InputRequired()])
admin = BooleanField(lazy_gettext(u'Administrator'))
Submit = SubmitField(lazy_gettext(u'Save'))
class UserSelfEditForm(FlaskForm):
old_password = PasswordField(lazy_gettext(u'Old password'),[validators.Optional()])
new_password = PasswordField(lazy_gettext(u'New password'),[EqualTo('confirm_password', message=lazy_gettext(u'Passwords must match'))])
confirm_password = PasswordField(lazy_gettext(u'Repeat password'))
language = SelectField(lazy_gettext(u'Language'),choices=language_choices)
submit = SubmitField(lazy_gettext(u'Save'))
@bp.route('/register', methods=('GET','POST'))
@roles_required('Admin')
def register():
form = UserCreationForm()
if form.validate_on_submit():
qry = db.session.query(User).filter(
User.username == form.username.data)
if qry.first() is not None:
flash(lazy_gettext(u'User {} is already registered.').format(form.username.data),'danger')
user = User(form.username.data, form.password.data, form.admin.data)
user.locale = form.language.data
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
return render_template('auth/register.html', form=form)
@bp.route('/init', methods=('GET','POST'))
def auth_init():
qry = db.session.query(User)
if qry.first() is not None:
flash(lazy_gettext(u'An admin user has already been created'),'danger')
return redirect(url_for('index'))
form = UserCreationForm()
if form.validate_on_submit():
user = User(form.username.data, form.password.data, True)
user.locale = form.language.data
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
return render_template('auth/register.html', form=form)
class LanguageCol(Col):
def td_format(self,content):
values = dict(language_choices)
if values[content] is not None:
return values[content]
else:
return lazy_gettext(u'Unknown Language')
class RoleCol(Col):
def td_format(self,content):
roles=[]
for role in content:
roles+=role.name +', '
return roles[:-2]
class UserTable(Table):
classes = ['table']
id = Col('Id')
username = Col(lazy_gettext(u'Username'))
roles = RoleCol(lazy_gettext(u'Roles'))
locale = LanguageCol(lazy_gettext(u'Language'))
edit = ButtonCol(lazy_gettext(u'Edit'), '.edit', url_kwargs=dict(userid='id'))
@bp.route('/userlist')
@roles_required('Admin')
def userlist():
results = User.query.all()
if not results:
flash(lazy_gettext(u'No users found!'))
return redirect('/')
else:
# display results
table = UserTable(results, no_items='There is nothing')
table.__html__()
table.border = True
return render_template('auth/userlist.html', table=table)
@bp.route('/user/<uuid:userid>', methods=['GET', 'POST'])
@login_required
def edit(userid):
qry = db.session.query(User).filter(User.id==userid)
user = qry.first()
if user:
if user.username == current_user.username:
form = UserSelfEditForm()
if form.validate_on_submit():
# Save modifications
if form.new_password.data is not '' and check_password_hash(user.password, form.old_password.data):
user.password = generate_password_hash(form.new_password.data)
elif form.new_password.data is not '':
flash(lazy_gettext(u'Incorrect old password'))
user.locale = form.language.data
print(current_user.locale)# = form.language.data
db.session.commit()
babrefresh()
flash(lazy_gettext(u'User \"{}\" updated successfully!').format(user.username))
return redirect(url_for('books.index'))
return render_template('auth/edit.html', form=form, username=user.username)
elif current_user.has_roles('Admin'):
form = UserEditForm()
if form.validate_on_submit():
# Save modifications
user.password = generate_password_hash(form.password.data)
user.admin = form.admin.data
db.session.commit()
flash(lazy_gettext(u'User updated successfully!'))
return redirect(url_for('.userlist'))
return render_template('auth/edit.html', form=form, username=user.username)
else:
flash(lazy_gettext(u"You don't have the rights to edit user: \"{username}\"").format(username=userid.hex))
redirect(url_for('.userlist'))
else:
flash(lazy_gettext(u'ERROR: User \"{username}\" doesn''t exist').format(username=userid.hex))
return redirect(url_for('.userlist'))
@bp.route('/user/<uuid:userid>/barcode', methods=['GET', 'POST'])
@login_required
def barcode(userid):
import barcode
from barcode import writer
from io import BytesIO, StringIO
qry = db.session.query(User).filter(User.id==userid)
user = qry.first()
if user:
if userid == current_user.id:
fp = BytesIO()
print(userid)
code128 = barcode.get_barcode_class('code128')
the_code = code128(userid.hex)
the_code.write(fp)
encoded_output = fp.getvalue().decode()
encoded_output = encoded_output[encoded_output.find('<svg'):]
fp.close()
return render_template('auth/barcode.html', encoded_output=encoded_output, username=current_user.username)
else:
flash(lazy_gettext(u"You don't have the rights to display: \"{user}\"'s barcode").format(user=userid.hex))
redirect(url_for('index'))
else:
flash(lazy_gettext(u'ERROR: User \"{userid}\" doesn''t exist').format(userid=userid.hex))
return redirect(url_for('index'))