· 6 years ago · Aug 27, 2019, 02:14 AM
1from flask import Flask, redirect, url_for, request
2from flask_sqlalchemy import SQLAlchemy
3from flask_login import LoginManager, current_user, login_user, logout_user # UserMixin
4from flask_admin import Admin, expose, AdminIndexView
5from flask_admin.helpers import validate_form_on_submit
6from flask_admin.contrib import sqla
7from flask_security import Security, SQLAlchemyUserDatastore, RoleMixin, UserMixin
8from flask_security.utils import hash_password
9from wtforms import Form, StringField, PasswordField, ValidationError
10from wtforms.validators import DataRequired
11from werkzeug.security import generate_password_hash, check_password_hash
12import os
13
14from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey
15
16# Create Flask application
17app = Flask(__name__)
18FILE = 'sample_db.sqlite'
19config = {
20 'DATABASE_FILE': FILE,
21 'SQLALCHEMY_DATABASE_URI': f'sqlite:///{FILE}',
22 'SECRET_KEY': '123456790',
23 'SQLALCHEMY_ECHO': True,
24 'SECURITY_PASSWORD_SALT': 'ATGUOHAELKiubahiughaerGOJAEGj',
25}
26app.config.update(config)
27
28# Instantiate extensions
29db = SQLAlchemy(app)
30
31# Define models
32if hasattr(db, 'Table'):
33 roles_users = db.Table(
34 'roles_users',
35 Column('user_id', Integer(), ForeignKey('user.id')),
36 Column('role_id', Integer(), ForeignKey('role.id'))
37 )
38
39
40class Role(db.Model, RoleMixin):
41 id = Column(Integer(), primary_key=True)
42 name = Column(String(80), unique=True)
43 description = Column(String(255))
44
45 def __str__(self):
46 return self.name
47
48
49class User(db.Model, UserMixin):
50 id = Column(Integer, primary_key=True)
51 username = Column(String(20), unique=True, nullable=False)
52 email = Column(String(120), unique=True, nullable=False)
53 password = Column(String(60), nullable=False)
54 image_file = Column(String(20), nullable=False, default='default.jpg')
55 # posts = db.relationship('Post', backref='author', lazy=True)
56 active = Column(Boolean())
57 confirmed_at = Column(DateTime())
58
59 if hasattr(db, 'relationship') and hasattr(db, 'backref'):
60 roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))
61
62 def __repr__(self):
63 return f"User('{self.username}', '{self.email}'')"
64
65
66# Setup Flask-Security
67user_datastore = SQLAlchemyUserDatastore(db, User, Role)
68security = Security(app, user_datastore)
69
70
71# Define login and registration forms (for flask-login)
72class LoginForm(Form):
73 username = StringField(validators=[DataRequired()])
74 password = PasswordField(validators=[DataRequired()])
75
76 def validate_login(self):
77 user = self.get_user()
78
79 if user is None:
80 raise ValidationError('Invalid user')
81
82 if not check_password_hash(user.password, self.password.data):
83 raise ValidationError('Invalid password')
84
85 def get_user(self):
86 return db.session.query(User).filter_by(username=self.username.data).first()
87
88
89class RegistrationForm(Form):
90 username = StringField(validators=[DataRequired()])
91 email = StringField()
92 password = PasswordField(validators=[DataRequired()])
93
94 def validate_login(self):
95 if db.session.query(User).filter_by(username=self.username.data).count() > 0:
96 raise ValidationError('Duplicate username')
97
98
99# Create customized model view class
100class MyModelView(sqla.ModelView):
101
102 def is_accessible(self):
103 return current_user.is_authenticated
104
105
106# Create customized index view class that handles login & registration
107class MyAdminIndexView(AdminIndexView):
108
109 @expose('/')
110 def index(self):
111 if not current_user.is_authenticated:
112 return redirect(url_for('.login_view'))
113 return super(MyAdminIndexView, self).index()
114
115 @expose('/login/', methods=('GET', 'POST'))
116 def login_view(self):
117 if current_user.is_authenticated:
118 return redirect(url_for('.index'))
119 form = LoginForm(request.form)
120 if validate_form_on_submit(form):
121 user = form.get_user()
122 if user is None:
123 print('User not found!')
124 return redirect(url_for('.index'))
125 elif not check_password_hash(user.password, form.password.data):
126 print('Invalid password!')
127 return redirect(url_for('.index'))
128 else:
129 login_user(user)
130 print('logged user in...')
131
132 if current_user.is_authenticated:
133 return redirect(url_for('.index'))
134 link = '<p>Don\'t have an account? <a href="' + url_for('.register_view') + '">Click here to register.</a></p>'
135 self._template_args['form'] = form
136 self._template_args['link'] = link
137 return super(MyAdminIndexView, self).index()
138
139 @expose('/register/', methods=('GET', 'POST'))
140 def register_view(self):
141 form = RegistrationForm(request.form)
142 if validate_form_on_submit(form):
143 user = User()
144
145 form.populate_obj(user)
146 user.password = generate_password_hash(form.password.data)
147
148 db.session.add(user)
149 db.session.commit()
150
151 login_user(user)
152 return redirect(url_for('.index'))
153 link = '<p>Already have an account? <a href="' + url_for('.login_view') + '">Click here to log in.</a></p>'
154 self._template_args['form'] = form
155 self._template_args['link'] = link
156 return super(MyAdminIndexView, self).index()
157
158 @expose('/logout/')
159 def logout_view(self):
160 logout_user()
161 return redirect(url_for('.index'))
162
163
164# Initialize flask-login
165def init_login():
166 login_manager = LoginManager()
167 login_manager.init_app(app)
168
169 # Create user loader function
170 @login_manager.user_loader
171 def load_user(user_id):
172 return db.session.query(User).get(user_id)
173
174
175# Flask views
176@app.route('/')
177def index():
178 return '<a href="/admin/">Click me to get to Admin!</a>'
179
180
181# Initialize flask-login
182init_login()
183
184# Create admin
185admin = Admin(app, 'Example: Auth', index_view=MyAdminIndexView(), base_template='my_master.html')
186
187# Add view
188admin.add_view(MyModelView(User, db.session))
189admin.add_view(MyModelView(Role, db.session))
190
191
192def build_sample_db():
193
194 db.drop_all()
195 db.create_all()
196 # test_user = User(username="test", email="bla@bla.com", password=generate_password_hash("test"))
197 # db.session.add(test_user)
198
199 with app.app_context():
200 user_role = Role()
201 super_user_role = Role()
202 db.session.add(user_role)
203 db.session.add(super_user_role)
204 db.session.commit()
205
206 user_datastore.create_user(
207 username='admin',
208 email='admin',
209 password=hash_password('admin') # ,
210 # roles=[user_role, super_user_role]
211 )
212 db.session.commit()
213 return
214
215
216if __name__ == '__main__':
217 # Build a sample db on the fly, if one does not exist yet.
218 app_dir = os.path.realpath(os.path.dirname(__file__))
219 database_path = os.path.join(app_dir, app.config['DATABASE_FILE'])
220 if not os.path.exists(database_path):
221 build_sample_db()
222 app.run(debug=True)