From 22ba2a7e0ac686b68f2fa1a252b35344610321d9 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Sat, 20 Apr 2019 18:44:49 -0500 Subject: [PATCH] Cleaning up Separated out commands into it's own file, so more can be added if needed. Pulled email templates into app template folder for easy access. Added Argon2 settings to .... settings --- app/__init__.py | 88 +------------------ app/commands.py | 81 +++++++++++++++++ app/local_settings_example.py | 14 +-- app/schemas.py | 1 - app/settings.py | 12 ++- .../flask_user/emails/base_message.html | 8 ++ .../flask_user/emails/base_message.txt | 7 ++ .../flask_user/emails/base_subject.txt | 1 + .../emails/confirm_email_message.html | 11 +++ .../emails/confirm_email_message.txt | 10 +++ .../emails/confirm_email_subject.txt | 3 + .../emails/invite_user_message.html | 10 +++ .../flask_user/emails/invite_user_message.txt | 9 ++ .../flask_user/emails/invite_user_subject.txt | 3 + .../emails/password_changed_message.html | 8 ++ .../emails/password_changed_message.txt | 12 +++ .../emails/password_changed_subject.txt | 3 + .../flask_user/emails/registered_message.html | 16 ++++ .../flask_user/emails/registered_message.txt | 15 ++++ .../flask_user/emails/registered_subject.txt | 3 + .../emails/reset_password_message.html | 12 +++ .../emails/reset_password_message.txt | 11 +++ .../emails/reset_password_subject.txt | 3 + .../emails/username_changed_message.html | 6 ++ .../emails/username_changed_message.txt | 10 +++ .../emails/username_changed_subject.txt | 3 + 26 files changed, 264 insertions(+), 96 deletions(-) create mode 100644 app/commands.py create mode 100644 app/templates/flask_user/emails/base_message.html create mode 100644 app/templates/flask_user/emails/base_message.txt create mode 100644 app/templates/flask_user/emails/base_subject.txt create mode 100644 app/templates/flask_user/emails/confirm_email_message.html create mode 100644 app/templates/flask_user/emails/confirm_email_message.txt create mode 100644 app/templates/flask_user/emails/confirm_email_subject.txt create mode 100644 app/templates/flask_user/emails/invite_user_message.html create mode 100644 app/templates/flask_user/emails/invite_user_message.txt create mode 100644 app/templates/flask_user/emails/invite_user_subject.txt create mode 100644 app/templates/flask_user/emails/password_changed_message.html create mode 100644 app/templates/flask_user/emails/password_changed_message.txt create mode 100644 app/templates/flask_user/emails/password_changed_subject.txt create mode 100644 app/templates/flask_user/emails/registered_message.html create mode 100644 app/templates/flask_user/emails/registered_message.txt create mode 100644 app/templates/flask_user/emails/registered_subject.txt create mode 100644 app/templates/flask_user/emails/reset_password_message.html create mode 100644 app/templates/flask_user/emails/reset_password_message.txt create mode 100644 app/templates/flask_user/emails/reset_password_subject.txt create mode 100644 app/templates/flask_user/emails/username_changed_message.html create mode 100644 app/templates/flask_user/emails/username_changed_message.txt create mode 100644 app/templates/flask_user/emails/username_changed_subject.txt diff --git a/app/__init__.py b/app/__init__.py index 8de0eab..3bf63f0 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -10,11 +10,7 @@ from flask_wtf.csrf import CSRFProtect from app.models import User, Role, UsersRoles from flask_user import user_registered - -import click -from flask.cli import with_appcontext -import datetime -from flask_user import current_app +from app.commands import init_db # Instantiate Flask extensions @@ -120,7 +116,8 @@ def init_email_error_handler(app): Initialize a logger to send emails on error-level messages. Unhandled exceptions will now send an email message to app.config.ADMINS. """ - if app.debug: return # Do not send error emails while developing + if app.debug: + return # Do not send error emails while developing # Retrieve email settings from app.config host = app.config['MAIL_SERVER'] @@ -150,82 +147,3 @@ def init_email_error_handler(app): app.logger.addHandler(mail_handler) # Log errors using: app.logger.error('Some error message') - -# TODO: separate out into a commands file - - -@click.command("init_db") -@with_appcontext -def init_db(): - """ Initialize the database.""" - - print('Initializing Database.') - print('Dropping all tables.') - db.drop_all() - print('Creating all tables.') - db.create_all() - create_users() - print('Database has been initialized.') - - -def create_users(): - """ Create users """ - - # Create all tables - db.create_all() - - # Adding roles - print('Creating Roles.') - admin_role = find_or_create_role('admin', u'Admin') # 1 - user_role = find_or_create_role('user', u'User') # 2 - reviewer = find_or_create_role('reviewer', u'Reviewer') # 3 - viewer = find_or_create_role('viewer', u'Viewer') # 4 - helper = find_or_create_role('helper', u'Helper') # 5 - - - # Add users - print('Creating Admin User.') - admin_user = find_or_create_user(u'Admin', u'Admin', u'Admin', u'admin@library.msstate.edu', 'Password1', u'CSE', u'net001', u'000-000-000', 1970, 1, 1, u'16623257668', u'US', u'Mississippi', u'Mississippi State', u'39762', u'395 Hardy Rd', None, admin_role) - - # Save to DB - db.session.commit() - - -def find_or_create_role(name, label): - """ Find existing role or create new role """ - role = Role.query.filter(Role.name == name).first() - if not role: - role = Role(name=name, label=label) - db.session.add(role) - return role - - -def find_or_create_user(first_name, middle_name, last_name, email, password, department, net_id, msu_id, b_year, b_month, b_day, prim_phone, country=u'US', administrative_area=u'Mississippi', locality=u'Mississippi State', postal_code=u'39762', thoroughfare=u'395 Hardy Rd', premise=None, role=None): - """ Find existing user or create new user """ - user = User.query.filter(User.email == email).first() - if not user: - b_time = datetime.datetime(b_year, b_month, b_day) - user = User(email=email, - first_name=first_name, - middle_name=middle_name, - last_name=last_name, - department=department, - net_id=net_id, - msu_id=msu_id, - birth_date=b_time.strftime('%B %d, %Y'), - prim_phone=prim_phone, - country=country, - administrative_area=administrative_area, - locality=locality, - postal_code=postal_code, - thoroughfare=thoroughfare, - premise=premise, - password=current_app.user_manager.password_manager.hash_password(password), - active=True, - email_confirmed_at=datetime.datetime.utcnow()) - if role: - user.roles.append(role) - db.session.add(user) - return user - - diff --git a/app/commands.py b/app/commands.py new file mode 100644 index 0000000..ba481a9 --- /dev/null +++ b/app/commands.py @@ -0,0 +1,81 @@ +import click +from flask.cli import with_appcontext +import datetime +from flask_user import current_app +from app import db +from app.models import Role, User + + +@click.command("init_db") +@with_appcontext +def init_db(): + """ Initialize the database.""" + + print('Initializing Database.') + print('Dropping all tables.') + db.drop_all() + print('Creating all tables.') + db.create_all() + create_users() + print('Database has been initialized.') + + +def create_users(): + """ Create users """ + + # Create all tables + db.create_all() + + # Adding roles + print('Creating Roles.') + admin_role = find_or_create_role('admin', u'Admin') # 1 + user_role = find_or_create_role('user', u'User') # 2 + reviewer = find_or_create_role('reviewer', u'Reviewer') # 3 + viewer = find_or_create_role('viewer', u'Viewer') # 4 + helper = find_or_create_role('helper', u'Helper') # 5 + + + # Add users + print('Creating Admin User.') + admin_user = find_or_create_user(u'Admin', u'Admin', u'Admin', u'admin@library.msstate.edu', 'Password1', u'CSE', u'net001', u'000-000-000', 1970, 1, 1, u'16623257668', u'US', u'Mississippi', u'Mississippi State', u'39762', u'395 Hardy Rd', None, admin_role) + + # Save to DB + db.session.commit() + + +def find_or_create_role(name, label): + """ Find existing role or create new role """ + role = Role.query.filter(Role.name == name).first() + if not role: + role = Role(name=name, label=label) + db.session.add(role) + return role + + +def find_or_create_user(first_name, middle_name, last_name, email, password, department, net_id, msu_id, b_year, b_month, b_day, prim_phone, country=u'US', administrative_area=u'Mississippi', locality=u'Mississippi State', postal_code=u'39762', thoroughfare=u'395 Hardy Rd', premise=None, role=None): + """ Find existing user or create new user """ + user = User.query.filter(User.email == email).first() + if not user: + b_time = datetime.datetime(b_year, b_month, b_day) + user = User(email=email, + first_name=first_name, + middle_name=middle_name, + last_name=last_name, + department=department, + net_id=net_id, + msu_id=msu_id, + birth_date=b_time.strftime('%B %d, %Y'), + prim_phone=prim_phone, + country=country, + administrative_area=administrative_area, + locality=locality, + postal_code=postal_code, + thoroughfare=thoroughfare, + premise=premise, + password=current_app.user_manager.password_manager.hash_password(password), + active=True, + email_confirmed_at=datetime.datetime.utcnow()) + if role: + user.roles.append(role) + db.session.add(user) + return user diff --git a/app/local_settings_example.py b/app/local_settings_example.py index ae78263..d9d6399 100644 --- a/app/local_settings_example.py +++ b/app/local_settings_example.py @@ -1,4 +1,3 @@ - import os # ***************************** @@ -10,9 +9,10 @@ DEBUG = True # Folders for uploading and supporting documents # THESE FOLDER MUST EXIST ON THE FILE SYSTEM -SIGNATURE_FOLDER = '/path/to/instance/signatures' -SUBMISSION_FOLDER = '/path/to/instance/submissions' -DOCUMENTS_FOLDER = '/path/to/instance/documents' +SIGNATURE_FOLDER = '/data/signatures' +SUBMISSION_FOLDER = '/data/submissions' +DOCUMENTS_FOLDER = '/data/documents' + # DO NOT use Unsecure Secrets in production environments # Generate a safe one with: # python -c "import os; print repr(os.urandom(24));" @@ -20,7 +20,7 @@ SECRET_KEY = 'This is an UNSECURE Secret. CHANGE THIS for production environment # SQLAlchemy settings SQLALCHEMY_DATABASE_URI = 'postgresql://user:password@host:port/database' -SQLALCHEMY_TRACK_MODIFICATIONS = False # Avoids a SQLAlchemy Warning +SQLALCHEMY_TRACK_MODIFICATIONS = False # Avoids a SQLAlchemy Warning # Flask-Mail settings # For smtp.gmail.com to work, you MUST set "Allow less secure apps" to ON in Google Accounts. @@ -33,7 +33,7 @@ MAIL_USERNAME = 'yourname@gmail.com' MAIL_PASSWORD = 'password' # Sendgrid settings -SENDGRID_API_KEY='place-your-sendgrid-api-key-here' +SENDGRID_API_KEY = 'place-your-sendgrid-api-key-here' # Flask-User settings USER_APP_NAME = 'Flask-User starter app' @@ -42,4 +42,4 @@ USER_EMAIL_SENDER_EMAIL = 'yourname@gmail.com' ADMINS = [ '"Admin One" ', - ] +] diff --git a/app/schemas.py b/app/schemas.py index 541229d..2045abc 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -1,5 +1,4 @@ from flask_marshmallow.sqla import ModelSchema -from app import ma from .models import User, Notification, Submission, Revision, Review diff --git a/app/settings.py b/app/settings.py index cb90308..f5d6df9 100644 --- a/app/settings.py +++ b/app/settings.py @@ -1,6 +1,4 @@ # Settings common to all environments (development|staging|production) -# Place environment specific settings in env_settings.py -# An example file (env_settings_example.py) can be used as a starting point # Application settings APP_NAME = "MSState Library ETD System" @@ -22,8 +20,16 @@ USER_ENABLE_EMAIL = True # Register with Email USER_ENABLE_REGISTRATION = True # Allow new users to register USER_REQUIRE_RETYPE_PASSWORD = True # Prompt for `retype password` in: USER_ENABLE_USERNAME = False # Register and Login with username -USER_PASSLIB_CRYPTCONTEXT_SCHEMES = ['argon2'] # argon2 for hashing +# Password hashing settings +USER_PASSLIB_CRYPTCONTEXT_SCHEMES = ['argon2'] # argon2 for password hashing +# I would suggest settings these to the maximum that you can. +USER_PASSLIB_CRYPTCONTEXT_KEYWORDS = dict(argon2__rounds=5, argon2__memory_cost=8192, argon2__max_threads=-1) +# rounds - This corresponds linearly to the amount of time hashing will take. +# memory_cost - Defines the memory usage in kibibytes. This corresponds linearly to the amount of memory hashing will take. +# max_threads - Maximum number of threads that will be used. -1 means unlimited; otherwise hashing will use `min(parallelism, max_threads)` threads. + +# Flask-User routing settings USER_AFTER_LOGIN_ENDPOINT = "main.index" USER_AFTER_LOGOUT_ENDPOINT = "main.signed_out" USER_AFTER_EDIT_USER_PROFILE_ENDPOINT = 'main.profile' diff --git a/app/templates/flask_user/emails/base_message.html b/app/templates/flask_user/emails/base_message.html new file mode 100644 index 0000000..183abc3 --- /dev/null +++ b/app/templates/flask_user/emails/base_message.html @@ -0,0 +1,8 @@ +

Dear {{ user.email }},

+ +{% block message %} +{% endblock %} + +

Sincerely,
+{{ app_name }} +

\ No newline at end of file diff --git a/app/templates/flask_user/emails/base_message.txt b/app/templates/flask_user/emails/base_message.txt new file mode 100644 index 0000000..5d63a0e --- /dev/null +++ b/app/templates/flask_user/emails/base_message.txt @@ -0,0 +1,7 @@ +Dear User, + +{% block message %} +{% endblock %} + +Sincerely, +{{ app_name }} diff --git a/app/templates/flask_user/emails/base_subject.txt b/app/templates/flask_user/emails/base_subject.txt new file mode 100644 index 0000000..7a6c3bf --- /dev/null +++ b/app/templates/flask_user/emails/base_subject.txt @@ -0,0 +1 @@ +{{ app_name }} - {% block subject %}{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/confirm_email_message.html b/app/templates/flask_user/emails/confirm_email_message.html new file mode 100644 index 0000000..e07a73a --- /dev/null +++ b/app/templates/flask_user/emails/confirm_email_message.html @@ -0,0 +1,11 @@ +{% extends 'flask_user/emails/base_message.html' %} + +{% block message %} +

You will need to confirm your email to start using {{ app_name }}.

+ +

If you initiated this confirmation, please click on the link below:
+    Confirm your email.

+ +

If you did not initiate this confirmation, you may safely ignore this email.

+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/confirm_email_message.txt b/app/templates/flask_user/emails/confirm_email_message.txt new file mode 100644 index 0000000..c707421 --- /dev/null +++ b/app/templates/flask_user/emails/confirm_email_message.txt @@ -0,0 +1,10 @@ +{% extends 'flask_user/emails/base_message.txt' %} + +{% block message %} +You will need to confirm your email to start using {{ app_name }}. + +If you initiated this registration, please visit the link below: + {{ confirm_email_link }} + +If you did not initiate this registration, you may safely ignore this email. +{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/confirm_email_subject.txt b/app/templates/flask_user/emails/confirm_email_subject.txt new file mode 100644 index 0000000..1f8c456 --- /dev/null +++ b/app/templates/flask_user/emails/confirm_email_subject.txt @@ -0,0 +1,3 @@ +{% extends 'flask_user/emails/base_subject.txt' %} + +{% block subject %}Email Confirmation{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/invite_user_message.html b/app/templates/flask_user/emails/invite_user_message.html new file mode 100644 index 0000000..7d1c393 --- /dev/null +++ b/app/templates/flask_user/emails/invite_user_message.html @@ -0,0 +1,10 @@ +{% extends 'flask_user/emails/base_message.html' %} + +{% block message %} + +

You have been invited to join {{ app_name }}!

+ +

To register an account, please click on the link below:
+    Join {{ app_name }}.

+ +{% endblock %} diff --git a/app/templates/flask_user/emails/invite_user_message.txt b/app/templates/flask_user/emails/invite_user_message.txt new file mode 100644 index 0000000..25986fa --- /dev/null +++ b/app/templates/flask_user/emails/invite_user_message.txt @@ -0,0 +1,9 @@ +{% extends 'flask_user/emails/base_message.txt' %} + +{% block message %} +You have been invited to join {{ app_name }}. + +To register an account, please click on the link below: + {{ accept_invitation_link }} + +{% endblock %} diff --git a/app/templates/flask_user/emails/invite_user_subject.txt b/app/templates/flask_user/emails/invite_user_subject.txt new file mode 100644 index 0000000..782705f --- /dev/null +++ b/app/templates/flask_user/emails/invite_user_subject.txt @@ -0,0 +1,3 @@ +{% extends 'flask_user/emails/base_subject.txt' %} + +{% block subject %}Invitation{% endblock %} diff --git a/app/templates/flask_user/emails/password_changed_message.html b/app/templates/flask_user/emails/password_changed_message.html new file mode 100644 index 0000000..8f62038 --- /dev/null +++ b/app/templates/flask_user/emails/password_changed_message.html @@ -0,0 +1,8 @@ +{% extends 'flask_user/emails/base_message.html' %} + +{% block message %} +

Your password has been changed.

+{% if user_manager.USER_ENABLE_FORGOT_PASSWORD %} +

If you did not initiate this password change, click here to reset it.

+{% endif %} +{% endblock %} diff --git a/app/templates/flask_user/emails/password_changed_message.txt b/app/templates/flask_user/emails/password_changed_message.txt new file mode 100644 index 0000000..b7448e6 --- /dev/null +++ b/app/templates/flask_user/emails/password_changed_message.txt @@ -0,0 +1,12 @@ +{% extends 'flask_user/emails/base_message.txt' %} + +{% block message %} +Your password has been changed. + +{% if user_manager.enable_forgot_password -%} +If you did not initiate this password change, click the link below to reset it. + {{ url_for('user.forgot_password', _external=True) }} +{% endif -%} +{% endblock %} + + diff --git a/app/templates/flask_user/emails/password_changed_subject.txt b/app/templates/flask_user/emails/password_changed_subject.txt new file mode 100644 index 0000000..1c3d89d --- /dev/null +++ b/app/templates/flask_user/emails/password_changed_subject.txt @@ -0,0 +1,3 @@ +{% extends 'flask_user/emails/base_subject.txt' %} + +{% block subject %}Your password has been changed{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/registered_message.html b/app/templates/flask_user/emails/registered_message.html new file mode 100644 index 0000000..a6a7396 --- /dev/null +++ b/app/templates/flask_user/emails/registered_message.html @@ -0,0 +1,16 @@ +{% extends 'flask_user/emails/base_message.html' %} + +{% block message %} + +

Thank you for registering with {{ app_name }}.

+ +{% if confirm_email_link -%} +

You will need to confirm your email next.

+ +

If you initiated this registration, please click on the link below:
+    Confirm your email.

+ +

If you did not initiate this registration, you may safely ignore this email.

+{%- endif %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/registered_message.txt b/app/templates/flask_user/emails/registered_message.txt new file mode 100644 index 0000000..c4c7876 --- /dev/null +++ b/app/templates/flask_user/emails/registered_message.txt @@ -0,0 +1,15 @@ +{% extends 'flask_user/emails/base_message.txt' %} + +{% block message %} +Thank you for registering with {{ app_name }}. + +{% if confirm_email_link -%} +You will need to confirm your email next. + +If you initiated this registration, please visit the link below: + {{ confirm_email_link }} + +If you did not initiate this registration, you may safely ignore this email. + +{%- endif %} +{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/registered_subject.txt b/app/templates/flask_user/emails/registered_subject.txt new file mode 100644 index 0000000..c9c6809 --- /dev/null +++ b/app/templates/flask_user/emails/registered_subject.txt @@ -0,0 +1,3 @@ +{% extends 'flask_user/emails/base_subject.txt' %} + +{% block subject %}{% if user_manager.enable_confirm_email and not user.confirmed_at %}Confirm your email{% else %}Thank you for registering{% endif %}{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/reset_password_message.html b/app/templates/flask_user/emails/reset_password_message.html new file mode 100644 index 0000000..45e9c4d --- /dev/null +++ b/app/templates/flask_user/emails/reset_password_message.html @@ -0,0 +1,12 @@ +{% extends 'flask_user/emails/base_message.html' %} + +{% block message %} + +

We have received your password reset request.

+ +

If you initiated this request, please click on the link below:
+    Reset password.

+ +

If you did not initiate this password reset, you may safely ignore this email.

+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/reset_password_message.txt b/app/templates/flask_user/emails/reset_password_message.txt new file mode 100644 index 0000000..265f485 --- /dev/null +++ b/app/templates/flask_user/emails/reset_password_message.txt @@ -0,0 +1,11 @@ +{% extends 'flask_user/emails/base_message.txt' %} + +{% block message %} +We have received your password reset request. + +If you initiated this request, please click on the link below: + {{ reset_password_link }} + +If you did not initiate this password reset, you may safely ignore this email. + +{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/reset_password_subject.txt b/app/templates/flask_user/emails/reset_password_subject.txt new file mode 100644 index 0000000..655b402 --- /dev/null +++ b/app/templates/flask_user/emails/reset_password_subject.txt @@ -0,0 +1,3 @@ +{% extends 'flask_user/emails/base_subject.txt' %} + +{% block subject %}Reset password{% endblock %} \ No newline at end of file diff --git a/app/templates/flask_user/emails/username_changed_message.html b/app/templates/flask_user/emails/username_changed_message.html new file mode 100644 index 0000000..f569fb2 --- /dev/null +++ b/app/templates/flask_user/emails/username_changed_message.html @@ -0,0 +1,6 @@ +{% extends 'flask_user/emails/base_message.html' %} + +{% block message %} +

Your username has been changed.

+

If you did not initiate this username change, please sign in (using your email address) and change your password.

+{% endblock %} diff --git a/app/templates/flask_user/emails/username_changed_message.txt b/app/templates/flask_user/emails/username_changed_message.txt new file mode 100644 index 0000000..1d4ee5c --- /dev/null +++ b/app/templates/flask_user/emails/username_changed_message.txt @@ -0,0 +1,10 @@ +{% extends 'flask_user/emails/base_message.txt' %} + +{% block message %} +Your username has been changed. + +If you did not initiate this username change, please sign in (using your email address) and change your password. + {{ url_for('user.login', _external=True) }} +{% endblock %} + + diff --git a/app/templates/flask_user/emails/username_changed_subject.txt b/app/templates/flask_user/emails/username_changed_subject.txt new file mode 100644 index 0000000..32a3602 --- /dev/null +++ b/app/templates/flask_user/emails/username_changed_subject.txt @@ -0,0 +1,3 @@ +{% extends 'flask_user/emails/base_subject.txt' %} + +{% block subject %}Your username has been changed{% endblock %} \ No newline at end of file