Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
d93ea21
Change characterization of a missing template file as a warning inste…
pjewald Feb 2, 2017
38e98d6
Merge pull request #19 from zfi/demo
zfi Feb 2, 2017
947d00b
Add artifacts from package operation to the .gitignore file.
pjewald Feb 7, 2017
41fbd19
Syncing local repo with parallaxinc demo branch
zfi Feb 7, 2017
c733908
Add example configuration file to project
pjewald Feb 27, 2017
ac628e3
Merge pull request #21 from zfi/demo
zfi Feb 27, 2017
61df32f
Refactor minor details
pjewald Apr 27, 2017
bdf5f0f
Add code for birthdate and parent email
pjewald Apr 27, 2017
d1223e8
Add application version number
pjewald Apr 27, 2017
45b4b4d
Add more COPPA fields
pjewald Apr 28, 2017
04f03fd
Correct error in date selection
pjewald Apr 28, 2017
4a11ee5
Add trap for missing SMTP server
pjewald May 3, 2017
631edee
Exclude local shell scripts
pjewald May 5, 2017
8b6a76a
Add missing mustache templates to project. Update missing SMTP server…
pjewald May 5, 2017
53557d4
Correct missing COPPA data block
pjewald May 8, 2017
6621b41
Add SponsorType enum to make code more readable
pjewald May 11, 2017
db7b859
Add code to send registration confirmation email to the correct email…
pjewald May 11, 2017
39224fc
Add templates for teachers and parent emails. Moved SponsorType to em…
pjewald May 11, 2017
3bfd977
Create parent templates. Rename teacher template path
pjewald May 12, 2017
9627ff9
Update code to handle COPPA compliant email templates
pjewald May 12, 2017
5eb6e21
Add logging to help track code paths.
pjewald May 12, 2017
afd17dd
Complete draft email to teacher to register a student.
pjewald May 12, 2017
6fe37c1
Updates to make the COPPA parent email work
pjewald May 16, 2017
49aec57
Bumping up the version number. 1.1.0 include support for US COPPA reg…
pjewald May 16, 2017
db032a7
Merge pull request #23 from zfi/20170227-COPPA-Register
paragitadmin May 16, 2017
282c643
Add parent notification for US-EN language. Add paragraph to link to …
pjewald May 19, 2017
ef71d2a
Add documentation and model version number
pjewald May 26, 2017
8143144
Hide build files
pjewald May 26, 2017
9210266
Merge branch 'demo' of github.com:zfi/Cloud-Session into demo
pjewald May 26, 2017
984dc7b
Merge pull request #24 from zfi/20170227-COPPA-Register
zfi May 26, 2017
642eebc
Add template macro for blocklyprop host. Updated templates to use the…
pjewald Jun 12, 2017
1fc363b
Merge pull request #25 from zfi/20170227-COPPA-Register
zfi Jun 12, 2017
cabbadc
Add to configuration file documentation
pjewald Jun 20, 2017
4c2d481
Add deployment scripts to ignore
pjewald Jun 20, 2017
4ff17d7
Update templates to use new email aliases for studdent and teacher. A…
pjewald Jun 20, 2017
d255f98
Merge pull request #26 from zfi/20170227-COPPA-Register
zfi Jun 20, 2017
d957386
Update email template to note seven day registration expiration.
pjewald Jun 20, 2017
8a53392
Add database migration scripts. 0001 and 0002 were moved from the Blo…
pjewald Jun 20, 2017
27e8c52
Add base database schema to project
pjewald Jun 20, 2017
a0bdaa2
Merge pull request #27 from zfi/20170227-COPPA-Register
zfi Jun 20, 2017
5562ce6
Update email message sent to parent of registering child.
pjewald Jun 21, 2017
6c60d2d
Update message text. Add logging to capture a failed template parse.
pjewald Jun 21, 2017
372e40d
Bumping version to 1.1.1
pjewald Jun 21, 2017
c7294bd
Merge pull request #28 from zfi/20170227-COPPA-Register
zfi Jun 21, 2017
59394bc
Add code to return a timestamp to indicate whent he next token will b…
pjewald Jun 27, 2017
e59f91d
Merge updates to demo branch
pjewald Jun 27, 2017
53361bf
Correct timestamp issue in error 470 handler
pjewald Jun 28, 2017
f6e5d55
Correct datetime string formatting error. Bump patch level
pjewald Jun 28, 2017
9e59ee8
Merge pull request #30 from zfi/demo
zfi Jun 28, 2017
7da4dca
Merge pull request #31 from zfi/demo
zfi Jul 10, 2017
8c275a6
Add database update script to exclusion list
pjewald Jul 14, 2017
8e99840
Merge branch 'demo' of github.com:parallaxinc/Cloud-Session into demo
pjewald Jul 19, 2017
3cbdf7f
Create script to dump cloudsession database. Create patch to change c…
pjewald Jul 24, 2017
16a3ccc
Update script to correct SQL errors
zfi Dec 5, 2017
1d14097
Add support for individual or alternate email confirmation address
pjewald Dec 7, 2017
4d8c75b
Update to support alternate email confirmation address
zfi Dec 7, 2017
9e6f7f1
Correct error where template used wrong email address in URL
zfi Dec 7, 2017
34cb4d2
Corrent error in merge process
zfi Dec 7, 2017
43b18e0
Corrent error where localhost was hard-coded into templates.
zfi Dec 7, 2017
388c5b6
Remove extra forwards slash in URL
zfi Dec 7, 2017
618d644
Correct local merge error
zfi Dec 7, 2017
2c4a02b
Merge pull request #32 from zfi/demo
zfi Dec 7, 2017
8fb8857
Add documentation to user account creation process
zfi Dec 15, 2017
0b48aed
Merge pull request #33 from zfi/demo
zfi Dec 21, 2017
89bf0de
Minor script updates that do not affect historical schema progression
pjewald Dec 29, 2017
7a02053
Merge pull request #34 from zfi/demo
zfi Dec 29, 2017
fff339a
Add try-catch block in email send to trap exceptions from the flask e…
pjewald Jan 5, 2018
22131a6
Merge pull request #37 from zfi/1.1
zfi Jan 5, 2018
0fa74af
Correct logic error that was using alternate email address even if it…
pjewald Jan 5, 2018
4009cd8
Merge pull request #38 from zfi/1.1
zfi Jan 5, 2018
512f43f
Update packages to supported versions. Update logging to less verbose…
pjewald Jan 5, 2018
ffdf2e3
Update packages to supported versions. Update logging to less verbose…
pjewald Jan 5, 2018
3f4354b
Merge pull request #40 from zfi/1.1
zfi Jan 5, 2018
c5463a5
Remove extraneous '/' from URL embedded in the templates.
pjewald Jan 6, 2018
cb5096d
Update templates to remove extraneous '/' character in the URL
zfi Jan 6, 2018
d77065c
Merge pull request #43 from zfi/1.1
zfi Jan 6, 2018
8e9b6b2
Add template variables to suplement plus sign in some email addresses
zfi Jan 16, 2018
6f68268
Merge pull request #44 from zfi/1.1.4
zfi Jan 17, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
*.sqlite
*.log
venv/
build.sh
CloudSession-Pkg.tar.gz

#################
## NetBeans
Expand All @@ -16,3 +18,11 @@ nbactions.xml
#################

.idea
build
copy-test
deploy-test
deploy2coppa
dbupdate.sh
/CloudSession-Templates.tar.gz
/copy-test
/deploy2coppa
8 changes: 7 additions & 1 deletion Failures.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,17 @@ def screen_name_already_in_use(screen_name):
}, 500


def rate_exceeded():
def rate_exceeded(time):
"""
Service requested to frequently.

time - string representing the date and time the service will be available again
"""
logging.debug('Failures: Rate exceeded')
return {
'success': False,
'message': 'Insufficient bucket tokens',
'data': time,
'code': 470
}, 500

Expand Down
3 changes: 1 addition & 2 deletions app/AuthToken/controllers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# Import the database object from the main app module
import json
import logging
import uuid
import datetime

import Failures

from app import db
from app.User import services as user_service

Expand Down
1 change: 0 additions & 1 deletion app/AuthToken/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# We will define this inside /app/__init__.py in the next sections.
from app import db


class AuthenticationToken(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
id_user = db.Column(db.BigInteger, db.ForeignKey('user.id'))
Expand Down
10 changes: 6 additions & 4 deletions app/Authenticate/controllers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# Import the database object from the main app module
import logging
import uuid
import datetime

import Failures

from app import db
from app.User import services as user_services
from app.RateLimiting import services as rate_limiting_services
Expand Down Expand Up @@ -66,7 +64,11 @@ def post(self):
'email': user.email,
'locale': user.locale,
'screenname': user.screen_name,
'authentication-source': user.auth_source
'authentication-source': user.auth_source,
'bdmonth': user.birth_month,
'bdyear': user.birth_year,
'parent-email': user.parent_email,
'parent-email-source': user.parent_email_source
}}

api.add_resource(AuthenticateLocalUser, '/local')
173 changes: 158 additions & 15 deletions app/Email/services.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,217 @@
from app import mail, app, db

from app import mail, app
from os.path import expanduser, isfile

from flask.ext.mail import Message
from flask_mail import Message
from app.User.coppa import Coppa, SponsorType

import pystache
import logging


"""
TODO: System documentation goes here
"""


def send_email_template_for_user(id_user, template, server, **kwargs):
from app.User.services import get_user

logging.info("Sending email to user: %s (%s)", id_user, template)
# Get a copy of the user record
logging.info("Checking for a valid user record for user ID: %s", id_user)
user = get_user(id_user)

if user is None:
logging.error("Cannot send email: Invalid user record")
return False
else:
logging.info("Valid record found for user: %s", user.id)

logging.info("Sending email to user: %s using template: '%s'.", user.email, template)

params = {}
for key, value in kwargs.items():
logging.debug("Logging parameter %s = %s", key, value)
params[key] = value

user = get_user(id_user)
if user is None:
return False

# The elements in the params array represent the data elements that are
# available to the email templates.
params['screenname'] = user.screen_name
params['email'] = user.email
params['registrant-email'] = user.email
params['sponsoremail'] = user.parent_email
params['blocklyprop-host'] = app.config['CLOUD_SESSION_PROPERTIES']['response.host']

# Default the recipient email address
user_email = user.email
coppa = Coppa()

# Send email to parent if user is under 13 years old
if template == 'confirm' and coppa.is_coppa_covered(user.birth_month, user.birth_year):
# Send email only to the sponsor address
user_email = user.parent_email
logging.info("COPPA account has a sponsor type of %s", user.parent_email_source)

if user.parent_email_source == SponsorType.TEACHER:
# Teacher handles the account confirmation
send_email_template_to_address(user_email, 'confirm-teacher', server, user.locale, params)
elif user.parent_email_source == SponsorType.PARENT or\
user.parent_email_source == SponsorType.GUARDIAN:
# Parent handles the account confirmation
send_email_template_to_address(user_email, 'confirm-parent', server, user.locale, params)
else:
logging.info("COPPA account %s has invalid sponsor type [%s]", user.id, user.parent_email_source)

return
elif template == 'reset' and coppa.is_coppa_covered(user.birth_month, user.birth_year):
# Send email only to the sponsor address
logging.info("COPPA account has a sponsor type of %s", user.parent_email_source)

# Send password reset to student and parent
send_email_template_to_address(user.email, 'reset-coppa', server, user.locale, params)
send_email_template_to_address(user.parent_email, 'reset-coppa', server, user.locale, params)
return
else:
# Registration not subject to COPPA regulations.
#
# Evaluate user wanting to use an alternate email address to register
# the account.
logging.info('Non-COPPA registration')
if user.parent_email_source == SponsorType.INDIVIDUAL and user.parent_email:
user_email = user.parent_email
logging.info('Individual sponsor email %s being used', user_email)

if user.parent_email:
user_email = user.parent_email
logging.info('Sponsor email %s being used', user_email)

send_email_template_to_address(user.email, template, server, user.locale, params)
send_email_template_to_address(user_email, template, server, user.locale, params)

return


def send_email_template_to_address(recipient, template, server, locale, params=None, **kwargs):
# Read templates
logging.info("Preparing email template: %s for %s", template, recipient)
params = params or {}

# Add any supplied arguments to the parameter dictionary
for key, value in kwargs.items():
params[key] = value

params['email'] = recipient
params['locale'] = locale

# Create a URI-friendly version of the email addresses
params['email-uri'] = _convert_email_uri(params['email'])
logging.info("Email address %s converted to %s",
params['email'],
params['email-uri']
)

params['registrant-email-uri'] = _convert_email_uri(params['registrant-email'])
logging.info("Registrant email address %s converted to %s",
params['registrant-email'],
params['registrant-email-uri']
)

params['sponsor-email-uri'] = _convert_email_uri(params['sponsoremail'])
logging.info("Sponsor email address %s converted to %s",
params['sponsoremail'],
params['sponsor-email-uri']
)

# Read templates
(subject, plain, rich) = _read_templates(template, server, locale, params)
# Add error checking here to detect any issues with parsing the template.

logging.info("Sending email to %s", params['email'])
send_email(recipient, subject, plain, rich)


def send_email(recipient, subject, email_text, rich_email_text=None):
logging.info('Creating email message package')
msg = Message(
recipients=[recipient],
subject=subject.rstrip(),
body=email_text,
html=rich_email_text,
sender=app.config['DEFAULT_MAIL_SENDER']
)
mail.send(msg)

# Attempt to send the email
try:
logging.info('Sending email message to server')
mail.send(msg)
except Exception as ex:
logging.error('Unable to send email')
logging.error('Error message: %s', ex.message)
return 1

logging.info('Email message was delivered to server')
return 0


def _read_templates(template, server, locale, params):
logging.info("Loading header text for template: %s", template)
header = _read_template(template, server, locale, 'header', params)

logging.info("Loading plain message text for template: %s", template)
plain = _read_template(template, server, locale, 'plain', params)

logging.info("Loading rich message text for template: %s", template)
rich = _read_template(template, server, locale, 'rich', params, True)

return header, plain, rich


def _read_template(template, server, locale, part, params, none_if_missing=False):
"""
Render a mustache template.

:param template: Base template name
:param server: Host server
:param locale: Language designator
:param part: Generic message type descriptor
:param params: Text string to replace tags embedded within the template
:param none_if_missing: Return 'none' if the requested template is not found

:return: Upon success, return a Renderer object. Return none or a general
error message if the none_is_missing flag is false
"""
template_file = expanduser("~/templates/%s/%s/%s/%s.mustache" % (locale, template, server, part))

if isfile(template_file):
logging.debug('Looking for template file: %s', template_file)

renderer = pystache.Renderer()
rendered = renderer.render_path(template_file, params)
#print(rendered)

logging.debug('Rendering the template file')
try:
rendered = renderer.render_path(template_file, params)
except Exception as ex:
logging.error('Unable to render template file %s', template_file)
logging.error('Error message: %s', ex.message)
return 'Template format error.'

logging.debug('Returning rendered template file.')
return rendered
else:
logging.error('Looking for template file: %s, but the file is missing', template_file)
logging.warn('Looking for template file: %s, but the file is missing', template_file)
if none_if_missing:
return None
else:
return 'Template missing'


def _convert_email_uri(email):
"""
Evaluate email address and replace any plus signs that may appear in the
portion of the address prior to the '@' with the literal '%2B'.

Standard web servers will convert any plus ('+') symbol to a space (' ')
anywhere where they may appear in the URL. This will allow functions upstream
to create a URI that contains an email address that, when submitted to a
server, will not be replaced with a space character.
"""
if "+" in email:
return email.replace("+", "%2B")
else:
return email
Loading