stc

a simple time card webapp
git clone _git@git.brennen.work:stc.git
Log | Files | Refs | README

commit e22804616be78b7f463ad9df7c10e331dbc1b79e
parent f527b58a30c766e70d57473b964fb59ea9e5022e
Author: Brennen T. Mazur <brennen@madis.cool>
Date:   Fri, 27 Jan 2023 09:06:35 -0700

fix load_user() and login related functions and methods

Diffstat:
Mapp/forms.py | 3+--
Mapp/models.py | 69+++++++++++++++++++++++++++++++++++++++++++--------------------------
Mapp/routes.py | 44+++++++++++++++++++++++++++++---------------
Mapp/templates/dashboard/punchclock/widget.html | 2+-
Mapp/templates/login.html | 1-
Mseeds.py | 15++++++++++++---
6 files changed, 86 insertions(+), 48 deletions(-)

diff --git a/app/forms.py b/app/forms.py @@ -10,11 +10,10 @@ from wtforms.validators import DataRequired, optional class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()]) - remember_me = BooleanField('Remember Me') login = SubmitField('Login') class PunchclockWidget(FlaskForm): - projectsSel = SelectField('Project', validators=[DataRequired()], coerce=str, choices=[]) + projectsSel = SelectField('Project', validators=[DataRequired()]) #clockin = currenttime lunchBox = BooleanField('Lunch') perdiumBox = BooleanField('Perdium') diff --git a/app/models.py b/app/models.py @@ -14,19 +14,54 @@ from werkzeug.security import generate_password_hash, check_password_hash #from app import db class User: + + def __init__(self, fname, mname, lname, email, phonenumber, branch, address, birthday, role): + self.fname = str(fname) + self.mname = str(mname) + self.lname = str(lname) + self.username = self.fname + self.mname + self.lname + self.email = email + self.phonenumber = int(phonenumber) + self.branch = str(branch) + self.address = address + self.birthday = birthday + self.role = role + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + + @staticmethod + def is_authenticated(): + return True + + @staticmethod + def is_active(): + return True + + @staticmethod + def is_anonymous(): + return False + + def get_id(self): + return self.username + - def start_session(self, user): - del user['password'] - session['logged_in'] = True - session['user'] = user - return jsonify(user), 200 + + #def start_session(self, user): + # del user['password'] + # session['logged_in'] = True + # session['user'] = user + #return jsonify(user), 200 def signup(self): print(request.form) user = { '_id': uuid.uuid4().hex, - 'username': request.form.get('Username'), - 'password': request.form.get('Password'), + 'username': request.form.get('username'), + 'password_hash': request.form.get('password'), 'role': request.form.get('Role'), 'location': request.form.get('Primary Location'), 'phone': request.form.get('Phone Number'), @@ -41,7 +76,7 @@ class User: def signout(self): session.clear() - return redirect('/loginModel') + return redirect('/login') # def loginModel(self): # user = db.user.find_one({ @@ -58,24 +93,6 @@ class User: assert v.isalnum(), 'Username must be alphanumeric' return v - @staticmethod - def is_authenticated(): - return True - - @staticmethod - def is_active(): - return True - - @staticmethod - def is_anonymous(): - return False - - def get_id(self): - return self.username - - @staticmethod - def check_password(password_hash, password): - return check_password_hash(password_hash, password) class Time: diff --git a/app/routes.py b/app/routes.py @@ -11,7 +11,6 @@ from flask_login import current_user, login_user, logout_user, login_required from app.models import User, Time, Fleet, Agreement, Projects OrganizationName = 'Youth Employment Program' # Maybe pass this as a value though the object for relevant pages??? -user_data = {'fname':'Brennen','lname':'Mazur','role':'Crew Lead','clocked_in':True} # DB Connection & LoginManager init mongo = PyMongo(app) @@ -38,36 +37,51 @@ def signout(): def hello(): return render_template('index.html',ORGNAME = OrganizationName) #This implimentation of ORGNAME is messy, maybe abstract to a defPage()? +@app.route("/logout", methods=['GIT']) +@login_required +def logout(): + logout_user() + return redirect(url_for('login')) + @app.route("/login", methods=['GET', 'POST']) def login(): + if current_user.is_authenticated: + return redirect(url_for('dashboard')) form = LoginForm() if form.validate_on_submit(): - login_user(user) - flask.flash('Logged in successfully.') - next = flask.request.args.get('next') - # is_safe_url should check url for safe redirects - # example here : http://flask.pocoo.org/snippets/62/ - if not is_safe_url(next): - return flask.abort(400) - - return flask.redirect(next or flask.url_for('dashboard')) + # check form value for identity in db, if found AND form password matches stored hash, create User object + u = mongo.db.user_collection.find_one({"username": form.username.data}) + if u and User.check_password(u['password'], form.password.data): + user_obj = User(fname=u['fname'],mname=u['mname'],lname=u['lname'],email=u['email'],branch=u['branch'],address=u['address'],birthday=u['birthday'],role=u['role'],phonenumber=u['phonenumber']) + #login with new user object + login_user(user_obj) + flask.flash('Logged in successfully.') + # check next redirect to stop cross-site-redirects, another example here : http://flask.pocoo.org/snippets/62/ + next = flask.request.args.get('next') + if not next or url_parse(next).netloc != '': + next = url_for('dashboard') + return redirect(next) + else: + flash("Invalid username or password") return render_template('login.html',form=form,ORGNAME = OrganizationName) +#load user_data into User object iff username == stored username @login_manager.user_loader def load_user(username): - u = mongo.db.Users.find_one({"Name": username}) + u = mongo.db.user_collection.find_one({"username": username}) if not u: return None - return User(username=u['name']) + return User(fname=u['fname'],mname=u['mname'],lname=u['lname'],email=u['email'],branch=u['branch'],address=u['address'],birthday=u['birthday'],role=u['role'],phonenumber=u['phonenumber']) @app.route("/dashboard") +@login_required def dashboard(): form=PunchclockWidget() currenttime=datetime.datetime.utcnow() - available_projects = [('stcapp','STC Webapp'),('yepsite','YEP Website'),('volday','Volunteer Day')] + available_projects = {'STC Webapp':'stcapp','YEP Website':'yepsite','Volunteer Day':'volday'} - return render_template('dashboard/layout.html',currenttime=currenttime,user=user_data,projects=available_projects,form=form,ORGNAME=OrganizationName) + return render_template('dashboard/layout.html',currenttime=currenttime,projects=available_projects,form=form,ORGNAME=OrganizationName) @app.route("/admin") def admin(): @@ -75,7 +89,7 @@ def admin(): @app.route("/hours")#modify to take userid ex. /hours<userid> for "admin" def hours():#userid goes into call to db to get user[] -> then returns formatted table (punchclock/index.html - return render_template ('dashboard/punchclock/index.html',user=user_data,ORGNAME=OrganizationName) + return render_template ('dashboard/punchclock/index.html',user=user,ORGNAME=OrganizationName) @app.route("/fleet") def fleet(): diff --git a/app/templates/dashboard/punchclock/widget.html b/app/templates/dashboard/punchclock/widget.html @@ -13,5 +13,5 @@ <p>{ form.perdiumBox() }} {{ form.perdiumBox.label }}</p> --> <button><a href="{{ url_for('hours') }}">Change Hours</a></button><!--routes to userhours.html--> - <h2>{{ user.fname }} {{ user.lname }}</h2><!-- something like currenttime above --> + <h2>{{ current_user.fname }} {{ current_user.lname }}</h2><!-- something like currenttime above --> </section> diff --git a/app/templates/login.html b/app/templates/login.html @@ -15,7 +15,6 @@ {{ form.password.label }}<br> {{ form.password(size=32) }} <!-- is it necessary to limit password lenght? --> </p> - <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> <p>{{ form.login() }}</p> </form> <!-- old version before structure change --> diff --git a/seeds.py b/seeds.py @@ -7,10 +7,18 @@ collection = db.get_collection('user_collection') x = collection.delete_many({}) # Database collections/documents +# Example create user via User constructor(all fields required) +u2 = User(fname='hiccup',mname='',lname='crawford',email='hiccup@example.com',branch='Dillon', address='705 N Railroad Ave, Dillon MT, 59725',birthday='2022-11-17',role='Dog',phonenumber='3074380491') +#create password +u2.set_password('hiccupmonster') +#document/json user_data, will have User.__init__() create userobj (routes.py) +#pw is 'password' user1 = { - 'username': 'Nikolas', - 'password': 'password', + 'fname': 'Nikolas', + 'mname': 'M', + 'lname': 'Mazur', + 'password_hash': 'pbkdf2:sha256:260000$DBIF9Dfq1OcsYwSk$37f5cc231ff2c97cc7a6b60f25c767380574f1c01cc17069da4f3e7e25ba3370', 'role': 'Developer', 'location': 'Lander', 'phone': 3074380460, @@ -20,9 +28,10 @@ user1 = { 'is_active':False } +#pw is 'password2' user2 = { 'username': 'Brennen', - 'password': 'password', + 'password_hash': 'pbkdf2:sha256:260000$ukazhSEG3m9xH2oL$5cc00ff3411f614720287c18f615d71578face70abc990ea5def89f520b0ac2c', 'role': 'Developer', 'location': 'Dillon', 'phone': 3074380491,