diff --git a/.gitignore b/.gitignore index fa45eca..ee8536d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,7 @@ build copy-test deploy-test deploy2coppa +dbupdate.sh /CloudSession-Templates.tar.gz +/copy-test +/deploy2coppa diff --git a/app/Email/services.py b/app/Email/services.py index 54d5df9..142959a 100644 --- a/app/Email/services.py +++ b/app/Email/services.py @@ -40,7 +40,7 @@ def send_email_template_for_user(id_user, template, server, **kwargs): params['sponsoremail'] = user.parent_email params['blocklyprop-host'] = app.config['CLOUD_SESSION_PROPERTIES']['response.host'] - #Default the recipient email address + # Default the recipient email address user_email = user.email coppa = Coppa() @@ -70,7 +70,13 @@ def send_email_template_for_user(id_user, template, server, **kwargs): send_email_template_to_address(user.parent_email, 'reset-coppa', server, user.locale, params) return else: - # Registration not subject to COPPA regulations + # Registration not subject to COPPA regulations. + # + # Evaluate user wanting to use an alternate email address to register + # the account. + if user.parent_email_source == SponsorType.INDIVIDUAL or user.parent_email: + user_email = user.parent_email + send_email_template_to_address(user_email, template, server, user.locale, params) return @@ -96,7 +102,7 @@ def send_email_template_to_address(recipient, template, server, locale, params=N def send_email(recipient, subject, email_text, rich_email_text=None): - + logging.info('Creating email message package') msg = Message( recipients=[recipient], subject=subject.rstrip(), @@ -104,7 +110,18 @@ def send_email(recipient, subject, email_text, rich_email_text=None): 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): diff --git a/app/LocalUser/controllers.py b/app/LocalUser/controllers.py index e3ec51d..4757bbc 100644 --- a/app/LocalUser/controllers.py +++ b/app/LocalUser/controllers.py @@ -18,11 +18,26 @@ class DoConfirm(Resource): + """ + Confirm and activate a user account. + + Args: + None + + Returns: + A JSON document with the key 'success' set to True if the operation + is successful. Otherwise the key 'success' is set to False and the + field 'code' is set to the HTTP error code that represents a specific + reason when the account confirmation was rejected. + + Raises: + None + """ def post(self): # Get values - email = request.form.get('email') - token = request.form.get('token') + email = request.form.get('email') # User account email address + token = request.form.get('token') # Token assigned to account during account registration # Validate required fields validation = Validation() @@ -52,9 +67,13 @@ def post(self): # Token is not for this user return {'success': False, 'code': 510} + # Set user account status to 'Confirmed' user.confirmed = True + # Delete the account confirmation token; it is no longer required db.session.delete(confirm_token) + + # Commit the user account changes db.session.commit() logging.info('LocalUser-controller: DoConfirm: success: %s', user.id) @@ -63,6 +82,15 @@ def post(self): class RequestConfirm(Resource): + """ + Send account confirmation request email to user + + Args: + param1: User account email address + + Returns: + JSON document detailing the success or failure of the request. + """ def get(self, email): # Get server URL diff --git a/app/__init__.py b/app/__init__.py index 2608dea..c5acf7f 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -25,7 +25,15 @@ app = Flask(__name__) # Application version (major,minor,patch-level) -version = "1.1.2" +version = "1.1.3" + +""" +Change Log + +1.1.3 Added documentation around the user account registration process. + +""" + db = None # Load basic configurations diff --git a/database/create_cloudsession_database.sql b/database/create_cloudsession_database.sql new file mode 100644 index 0000000..b404f2f --- /dev/null +++ b/database/create_cloudsession_database.sql @@ -0,0 +1,7 @@ +/* + * Create the cloudsession database + */ + +CREATE DATABASE IF NOT EXISTS `cloudsession` + CHARACTER SET = utf8 + COLLATE = utf8_general_ci; diff --git a/cloudsession-schema.sql b/database/create_cloudsession_tables.sql similarity index 50% rename from cloudsession-schema.sql rename to database/create_cloudsession_tables.sql index f4f7c00..fb72912 100644 --- a/cloudsession-schema.sql +++ b/database/create_cloudsession_tables.sql @@ -1,26 +1,36 @@ --- MySQL dump 10.13 Distrib 5.6.24, for Win64 (x86_64) +/* + * Base Cloud Session database schema. + */ + +USE cloudsession; + + +-- +-- Table structure for table `user` -- --- Host: localhost Database: cloudsession --- ------------------------------------------------------ --- Server version 5.7.7-rc-log -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `email` varchar(250) DEFAULT NULL, + `password` varchar(100) DEFAULT NULL, + `salt` varchar(50) DEFAULT NULL, + `auth_source` varchar(250) DEFAULT NULL, + `locale` varchar(50) DEFAULT NULL, + `blocked` tinyint(1) DEFAULT NULL, + `confirmed` tinyint(1) DEFAULT NULL, + `screen_name` varchar(250) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `email` (`email`) +) ENGINE=InnoDB + AUTO_INCREMENT=0 + DEFAULT CHARSET=utf8; + -- -- Table structure for table `authentication_token` -- DROP TABLE IF EXISTS `authentication_token`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; CREATE TABLE `authentication_token` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `id_user` bigint(20) DEFAULT NULL, @@ -33,16 +43,15 @@ CREATE TABLE `authentication_token` ( UNIQUE KEY `token` (`token`), KEY `id_user` (`id_user`), CONSTRAINT `authentication_token_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `user` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; +) ENGINE=InnoDB + AUTO_INCREMENT=0 + DEFAULT CHARSET=utf8; -- -- Table structure for table `bucket` -- DROP TABLE IF EXISTS `bucket`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; CREATE TABLE `bucket` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `id_user` bigint(20) DEFAULT NULL, @@ -52,17 +61,15 @@ CREATE TABLE `bucket` ( PRIMARY KEY (`id`), UNIQUE KEY `user_type_unique` (`id_user`,`type`), CONSTRAINT `bucket_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `user` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; +) ENGINE=InnoDB + AUTO_INCREMENT=0 + DEFAULT CHARSET=utf8; -- -- Table structure for table `confirmtoken` -- - DROP TABLE IF EXISTS `confirm_token`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; CREATE TABLE `confirm_token` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `id_user` bigint(20) DEFAULT NULL, @@ -72,16 +79,16 @@ CREATE TABLE `confirm_token` ( UNIQUE KEY `id_user` (`id_user`), UNIQUE KEY `token` (`token`), CONSTRAINT `confirm_token_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `user` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; +) ENGINE=InnoDB + AUTO_INCREMENT=0 + DEFAULT CHARSET=utf8; + -- -- Table structure for table `resettoken` -- DROP TABLE IF EXISTS `reset_token`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; CREATE TABLE `reset_token` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `id_user` bigint(20) DEFAULT NULL, @@ -91,38 +98,6 @@ CREATE TABLE `reset_token` ( UNIQUE KEY `id_user` (`id_user`), UNIQUE KEY `token` (`token`), CONSTRAINT `reset_token_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `user` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `user` --- - -DROP TABLE IF EXISTS `user`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `email` varchar(250) DEFAULT NULL, - `password` varchar(100) DEFAULT NULL, - `salt` varchar(50) DEFAULT NULL, - `auth_source` varchar(250) DEFAULT NULL, - `locale` varchar(50) DEFAULT NULL, - `blocked` tinyint(1) DEFAULT NULL, - `confirmed` tinyint(1) DEFAULT NULL, - `screen_name` varchar(250) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `email` (`email`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2015-08-18 20:24:57 +) ENGINE=InnoDB + AUTO_INCREMENT=0 + DEFAULT CHARSET=latin1; diff --git a/database/patches/0001-add-user-coach.sql b/database/patches/0001-add-user-coach.sql index 7e44c66..8c1b1a6 100644 --- a/database/patches/0001-add-user-coach.sql +++ b/database/patches/0001-add-user-coach.sql @@ -1,4 +1,5 @@ /* * Add coach email address field to support email cc option. */ -ALTER TABLE user ADD COLUMN coach_email VARCHAR(250) AFTER screen_name; +USE cloudsession; +ALTER TABLE cloudsession.user ADD COLUMN coach_email VARCHAR(250) AFTER screen_name; diff --git a/database/patches/0004-update-cs-default-char-col.sql b/database/patches/0004-update-cs-default-char-col.sql new file mode 100644 index 0000000..9f22a5f --- /dev/null +++ b/database/patches/0004-update-cs-default-char-col.sql @@ -0,0 +1,82 @@ +/* +Script: 0004-update-cs-default-char-col.sql + +This script corrects an issue in the cloud session database +where the default character set and collation were set to +'latin1' and 'latin1_swedish_ci'. These settings should be +'utf8' and 'utf8_general_ci'. + +This script updates the character set and collation settings +on the cloudsession database, all cloudsession tables and +affected columns within each of these tables. + */ + +# Select the target database +USE cloudsession; + +# Set the database defaults +# This also sets the collation for individual table columns +ALTER DATABASE cloudsession CHARACTER SET utf8 COLLATE utf8_general_ci; + +# latin1_general_ci +# ALTER DATABASE cloudsession CHARACTER SET latin1 COLLATE latin1_general_ci; + +# Update the authentication_token table +SET foreign_key_checks = 0; +# Default table settings +ALTER TABLE cloudsession.authentication_token DEFAULT CHARACTER SET utf8; + +# Column settings +ALTER TABLE cloudsession.authentication_token MODIFY browser VARCHAR(200) CHARACTER SET utf8; +ALTER TABLE cloudsession.authentication_token MODIFY server VARCHAR(1000) CHARACTER SET utf8; +ALTER TABLE cloudsession.authentication_token MODIFY ip_address VARCHAR(200) CHARACTER SET utf8; +ALTER TABLE cloudsession.authentication_token MODIFY token VARCHAR(200) CHARACTER SET utf8; + +SET foreign_key_checks = 1; + + +# Update the bucket table +SET foreign_key_checks = 0; +ALTER TABLE cloudsession.bucket DEFAULT CHARACTER SET utf8; + +# Reset fields +ALTER TABLE cloudsession.bucket MODIFY type VARCHAR(200) CHARACTER SET utf8; + +SET foreign_key_checks = 1; + + +# Update the confirm_token table +SET foreign_key_checks = 0; +ALTER TABLE cloudsession.confirm_token DEFAULT CHARACTER SET utf8; + +# Reset fields +ALTER TABLE cloudsession.confirm_token MODIFY token VARCHAR(200) CHARACTER SET utf8; + +SET foreign_key_checks = 1; + + +# Update the reset_token table +SET foreign_key_checks = 0; +ALTER TABLE cloudsession.reset_token DEFAULT CHARACTER SET utf8; + +# Reset fields +ALTER TABLE cloudsession.reset_token MODIFY token VARCHAR(200) CHARACTER SET utf8; + +SET foreign_key_checks = 1; + + +# Update the user table +SET foreign_key_checks = 0; +ALTER TABLE cloudsession.user DEFAULT CHARACTER SET utf8; + +# Reset fields +ALTER TABLE cloudsession.user MODIFY email VARCHAR(250) CHARACTER SET utf8; +ALTER TABLE cloudsession.user MODIFY password VARCHAR(100) CHARACTER SET utf8; +ALTER TABLE cloudsession.user MODIFY salt VARCHAR(50) CHARACTER SET utf8; +ALTER TABLE cloudsession.user MODIFY auth_source VARCHAR(250) CHARACTER SET utf8; +ALTER TABLE cloudsession.user MODIFY locale VARCHAR(50) CHARACTER SET utf8; +ALTER TABLE cloudsession.user MODIFY screen_name VARCHAR(250) CHARACTER SET utf8; +ALTER TABLE cloudsession.user MODIFY parent_email VARCHAR(250) CHARACTER SET utf8; + +SET foreign_key_checks = 1; + diff --git a/database/tools/dumpdb.sh b/database/tools/dumpdb.sh new file mode 100755 index 0000000..cc2a24e --- /dev/null +++ b/database/tools/dumpdb.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# +# Dump the local cloudsession database to a .sql file +# + +echo 'Enter the MySQL user account password below' +mysqldump --single-transaction -u blocklydb -p cloudsession > cs-backup.sql + diff --git a/templates/en/confirm-parent/blocklyprop/plain.mustache b/templates/en/confirm-parent/blocklyprop/plain.mustache index ff72128..8c7de6c 100644 --- a/templates/en/confirm-parent/blocklyprop/plain.mustache +++ b/templates/en/confirm-parent/blocklyprop/plain.mustache @@ -11,21 +11,48 @@ }} Hello, -A person under age 13 has requested a new account on the Parallax BlocklyProp web site http://{{blocklyprop-host}} under the screen name {{screenname}}. In the request, your email address was provided as the parent or guardian of the requester. - -BlocklyProp is a free, online programming tool designed for education. See Getting Started with BlocklyProp for more information. - -Why are we sending this? In the US, the federal Children's Online Privacy Protection Act (COPPA) requires that we communicate with a parent or guardian of any person under age 13 that requests a BlocklyProp account. A full copy of our Child Privacy Policy is available online at: http://{{blocklyprop-host}}/blockly/privacy-policy - -To complete your child's account registration, please copy and past this link into your browser to confirm your email address: - +Someone, perhaps your child, has requested a new account on the Parallax +BlocklyProp web site (http://blockly.parallax.com) under the screen name +{{screenname}}. When the account was created, your email address was +submitted as the parent or guardian of the registrant. If this is incorrect, +please accept our apologies. There is nothing more you need to do. The +request will automatically expire. + +Why are we sending this? In the US, the federal Children's Online Privacy +Protection Act, known as COPPA, requires that we communicate with a parent +or guardian if the person registering an BlocklyProp account is under the +age of 13. The Act allows you to decline the registration request. If you +choose this option, we will immediately remove the registration information +and the associated account. If you choose to confirm the the request and +activate the account, you may close the account at any time by clicking on +the link provided below. This action will close the account and remove any +projects that are associated with the account. + +A full copy of our Child Privacy Policy is available online at: +http://{{blocklyprop-host}}/blockly/child-privacy-policy + +BlocklyProp is a free, online programming tool designed for education. See +Getting Started with BlocklyProp for more information. + + +Confirm account registration: +Copy and paste into your browser http://{{blocklyprop-host}}/blockly/confirm?locale={{locale}}&email={{registrant-email}}&token={{token}} to confirm your email address. -If the above link is unable to complete the registration, please go to http://{{blocklyprop-host}}/blockly/confirm and enter your email address and the token: {{token}} +If the above link is unable to complete your registration, please go to +http://{{blocklyprop-host}}/blockly/confirm and enter your email address and the token: {{token}} + +If the above link is unable to complete the registration, please go to +http://{{blocklyprop-host}}/blockly/confirm and enter your email address and the token: {{token}} -If you do NOT want to complete your child's account registration, you need do nothing more; this confirmation request will automatically expire in 7 days and the account will not be created. +If you do NOT want to complete your child's account registration, you need do +nothing more; this confirmation request will automatically expire in 7 days and +the account will not be created. -If you do complete your child's account registration, you may close the account in the future. Email a request for closure and your child's screen name to blocklyadmin@parallax.com. We will confirm your request and then close the account and remove any projects that are associated with the account. +If you do complete your child's account registration, you may close the account +in the future. Email a request for closure and your child's screen name to +blocklyadmin@parallax.com. We will confirm your request and then close the account +and remove any projects that are associated with the account. Regards, diff --git a/templates/en/confirm/blocklyprop/plain.mustache b/templates/en/confirm/blocklyprop/plain.mustache index e39fe63..d414bf0 100644 --- a/templates/en/confirm/blocklyprop/plain.mustache +++ b/templates/en/confirm/blocklyprop/plain.mustache @@ -1,9 +1,9 @@ {{! This is the text body of the email}} Dear {{screenname}}, -Please go to http://{{blocklyprop-host}}/blockly/confirm?locale={{locale}}&email={{email}}&token={{token}} to confirm your email address. +Please go to http://{{blocklyprop-host}}//blockly/confirm?locale={{locale}}&email={{registrant-email}}&token={{token}} to confirm your email address. -If the url does not work, please go to http://{{blocklyprop-host}}/blockly/confirm and enter your email address and the token: {{token}} +If the url does not work, please go to http://{{blocklyprop-host}}//blockly/confirm, then enter your email address ({{email}}), and the token: {{token}} The Parallax team