-
Notifications
You must be signed in to change notification settings - Fork 4
/
app.py
executable file
·192 lines (162 loc) · 6.59 KB
/
app.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
import os
import base64
from flask import Flask, render_template, redirect, url_for, flash, session, \
abort
from werkzeug.security import generate_password_hash, check_password_hash
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager, UserMixin, login_user, logout_user, \
current_user
from flask.ext.bootstrap import Bootstrap
from flask.ext.wtf import Form
from wtforms import StringField, PasswordField, SubmitField, RadioField
from wtforms.validators import Required, Length, EqualTo
from random import randint
from twilio.rest import TwilioRestClient
import twilio
# create application instance
app = Flask(__name__)
app.config.from_object('config')
# initialize extensions
bootstrap = Bootstrap(app)
db = SQLAlchemy(app)
lm = LoginManager(app)
# Send the token via SMS or Voice depending of the user preferred method
def sendToken(username, token):
user = User.query.filter_by(username=username).first()
client = TwilioRestClient()
if user.method == 'SMS':
try:
message = client.messages.create(
body="Your token is:" + str(token), # Use the token to complete login
to=user.phone,
from_= app.config['PHONE_NUMBER'],
)
flash('Token sent with success !!')
except twilio.TwilioRestException as e:
print e
flash(u'Error while sending the token', 'error')
elif user.method == 'Voice':
try:
call = client.calls.create(to=user.phone, from_=app.config['PHONE_NUMBER'],
url="http://twimlets.com/message?Message%5B0%5D=Your%20token%20is%20"+str(token)+"&")
flash('Token sent with success !!')
except twilio.TwilioRestException as e:
print e
flash(u'Error while sending the token', 'error')
# TODO Generate more secure token
def generateToken():
#return randint(100000, 999999)
return "123456"
class User(UserMixin, db.Model):
"""User model."""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True)
password_hash = db.Column(db.String(128))
phone = db.Column(db.String(64))
method = db.Column(db.String(16))
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
@lm.user_loader
def load_user(user_id):
"""User loader callback for Flask-Login."""
return User.query.get(int(user_id))
class RegisterForm(Form):
"""Registration form."""
username = StringField('Username', validators=[Required(), Length(1, 64)])
password = PasswordField('Password', validators=[Required()])
password_again = PasswordField('Password again',
validators=[Required(), EqualTo('password')])
phone = StringField('Phone', validators=[Required(), Length(1, 64)])
method = RadioField('Preferred method:', choices=[('SMS','You will receice the code in SMS'), \
('Voice','You will receive the code in a Call')])
submit = SubmitField('Register')
class LoginForm(Form):
"""Login form."""
username = StringField('Username', validators=[Required(), Length(1, 64)])
password = PasswordField('Password', validators=[Required()])
submit = SubmitField('Login')
class TwoFactorForm(Form):
"""Verification code form."""
token = StringField('Token', validators=[Required(), Length(6, 6)])
submit = SubmitField('Verification')
@app.route('/')
def index():
return render_template('index.html')
@app.route('/voice')
def voice():
return render_template('call.xml')
@app.route('/register', methods=['GET', 'POST'])
def register():
"""User registration route."""
if current_user.is_authenticated():
# if user is logged in we get out of here
return redirect(url_for('index'))
form = RegisterForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is not None:
flash('Username already exists.')
return redirect(url_for('register'))
# add new user to the database
user = User(username=form.username.data, \
password=form.password.data, \
phone=form.phone.data, \
method=form.method.data)
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
return render_template('register.html', form=form)
# TODO Limit the number of false request (brute force prevention)
@app.route('/verification', methods=['GET', 'POST'])
def verification():
"""two factor auth route."""
if current_user.is_authenticated():
# if user is logged in we get out of here
return redirect(url_for('index'))
form = TwoFactorForm()
if form.validate_on_submit():
user = User.query.filter_by(username=session['username']).first()
if session['token'] == form.token.data:
# log user in
login_user(user)
flash('You are now logged in!')
return redirect(url_for('index'))
flash(' Invalid token.')
return redirect(url_for('verification'))
return render_template('verification.html', form=form)
@app.route('/login', methods=['GET', 'POST'])
def login():
"""User login route."""
if current_user.is_authenticated():
# if user is logged in we get out of here
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.verify_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
# redirect to the two-factor auth page, passing token in the session
session['username'] = form.username.data
session['token'] = generateToken()
sendToken(session['username'], session['token'])
return redirect(url_for('verification'))
return render_template('login.html', form=form)
@app.route('/logout')
def logout():
"""User logout route."""
logout_user()
return redirect(url_for('index'))
# create database tables if they don't exist yet
db.create_all()
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)