From d2a172d79c9e7b81a3b9247a6944f570782ed7d3 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Fri, 7 Oct 2016 16:36:40 +0200 Subject: [PATCH] Simplify email verification to a single address and add docs Issue #339 --- .../plugins/email_whitelist/email_verify.py | 4 +- .../email_whitelist/email_verify_tbl.py | 22 +++-------- .../email_whitelist/impl_email_whitelist.py | 13 ++----- .../juliabox/plugins/email_whitelist/info.md | 39 +++++++++++++++++++ 4 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 engine/src/juliabox/plugins/email_whitelist/info.md diff --git a/engine/src/juliabox/plugins/email_whitelist/email_verify.py b/engine/src/juliabox/plugins/email_whitelist/email_verify.py index 7e45c17..aa3ea89 100644 --- a/engine/src/juliabox/plugins/email_whitelist/email_verify.py +++ b/engine/src/juliabox/plugins/email_whitelist/email_verify.py @@ -51,7 +51,7 @@ def get(self): return if verification_code == None: - record = EmailVerifyDB(user_id, "pending_email_form_response", create=False) + record = EmailVerifyDB(user_id) record.set_email(email) base_uri = self.request.protocol + "://" + self.request.host + self.request.uri.split('?')[0] @@ -64,7 +64,7 @@ def get(self): self.render(os.path.join(EmailWhitelistHandler.TEMPLATE_PATH, "message.tpl"), cfg=JBoxCfg.nv, message="Email sent. Please click the link in the mail.") else: - record = EmailVerifyDB(user_id, email, create=False) + record = EmailVerifyDB(user_id) if record.verify(verification_code): s = dict(error="", success="Verification OK, please log in again", info="", pending_activation=False, user_id="") self.rendertpl("index.tpl", cfg=JBoxCfg.nv, state=s) diff --git a/engine/src/juliabox/plugins/email_whitelist/email_verify_tbl.py b/engine/src/juliabox/plugins/email_whitelist/email_verify_tbl.py index 4067509..53a9095 100644 --- a/engine/src/juliabox/plugins/email_whitelist/email_verify_tbl.py +++ b/engine/src/juliabox/plugins/email_whitelist/email_verify_tbl.py @@ -17,34 +17,22 @@ class EmailVerifyDB(JBPluginDB): KEYS_TYPES = [JBPluginDB.VCHAR] TYPES = [JBPluginDB.VCHAR, JBPluginDB.VCHAR, JBPluginDB.INT] - def __init__(self, user_id, email, create=False): - count = self.query_count(user_id__eq=user_id, email__eq=email) - if count > 0 and create: - raise RuntimeError("Email verify record exists, but create was requested") - if count == 0 and (not create): - raise RuntimeError("Email verify record does not exist") + def __init__(self, user_id): + count = self.query_count(user_id__eq=user_id) + create = (count == 0) if create: data = { 'user_id': user_id, - 'email': email, + 'email': '', 'verification_code': gen_random_secret(), 'is_verified': 0 } self.create(data) - self.item = self.fetch(user_id=user_id, email=email) + self.item = self.fetch(user_id=user_id) self.is_new = create - @classmethod - def get_verified_emails(cls, user_id): - records = cls.query(user_id__eq=user_id, is_verified__eq=1) - verified_email_list = [] - for rec in records: - verified_email_list.append(rec['email']) - return verified_email_list - - def set_email(self, email): self.set_attrib('email', email) self.save() diff --git a/engine/src/juliabox/plugins/email_whitelist/impl_email_whitelist.py b/engine/src/juliabox/plugins/email_whitelist/impl_email_whitelist.py index ce7dea4..0210813 100644 --- a/engine/src/juliabox/plugins/email_whitelist/impl_email_whitelist.py +++ b/engine/src/juliabox/plugins/email_whitelist/impl_email_whitelist.py @@ -54,15 +54,10 @@ def process_user_id(handler, user_id): if EmailWhitelistHandler.is_whitelisted(user_id): return True - # Check if any of the users verified email addresses match - verified_emails = EmailVerifyDB.get_verified_emails(user_id) - for allowed_mail in EmailWhitelistHandler.WHITELIST: - for verified_email in verified_emails: - if EmailWhitelistHandler.is_whitelisted(verified_email): - return True - - # No match, create a pending email verify request - EmailVerifyDB(user_id, "pending_email_form_response", create=True) + # No match on user_id, create a pending email verify request if needed + verify_record = EmailVerifyDB(user_id) + if verify_record.is_verified(): + return True handler.render(os.path.join(EmailWhitelistHandler.TEMPLATE_PATH, "email_whitelist.tpl"), cfg=JBoxCfg.nv, user_id=user_id, message="Please enter white-listed email as per tutor instructions:") diff --git a/engine/src/juliabox/plugins/email_whitelist/info.md b/engine/src/juliabox/plugins/email_whitelist/info.md new file mode 100644 index 0000000..ae5a383 --- /dev/null +++ b/engine/src/juliabox/plugins/email_whitelist/info.md @@ -0,0 +1,39 @@ +# Email whitelist plugin + +Post-auth plugin to check if an email address used by the user matches a whitelisted pattern. The procedure is as follows: + +1. Verify the `user_id` against the whitelist and allow the user if it matches +2. Check if the user has a verified alternate email, and if so allow access +2. If not, ask the user for an additional email address +3. Generate a random secret and email it to this address, embedded in a weblink +4. Upon clicking the link in the email, the address is verified and the user is directed to the login page +5. From now step 2 will succeed automatically upon successful authentication + +The plugin creates a new database table `jbox_email_verify` to store the verified email addresses. It also requires a configured plugin for sending email, and a config entry `email_whitelist` with a list of domains or email addresses in `allowed_addresses`. It checks that the given email address ends with the given pattern, ignoring case. This allows whitelisting either domains or individual addresses. + +Example config: + +``` +{ +#... other config as needed ... +"email_whitelist": { + "allowed_addresses": [ + "julia.org", + "user@some.com" + ] +}, +"user_activation": { + "sender": "email@fromaddress.com", + "sender_password": "", + "smtp_url": "smtp.mydomain.com", + "smtp_port_no": 587, + "max_24hrs": 100, + "max_rate_per_sec": 1 +}, +"plugins": [ + #... other plugins as necessary ... + "juliabox.plugins.email_whitelist", + "juliabox.plugins.sendmail_smtp" +] +} +```