Description: Add support for requiring new accounts to be verified by email Origin: http://moinmo.in/MoinMoinPatch/VerifyAccountCreationByEmail Author: Steve McIntyre Last-Update: 2013-09-04 --- ./MoinMoin/action/newaccount.py 2014-10-17 12:45:32.000000000 -0700 +++ ./MoinMoin/action/newaccount.py 2016-01-15 13:53:33.209638000 -0800 @@ -12,5 +12,27 @@ from MoinMoin.security.textcha import TextCha from MoinMoin.auth import MoinAuth +from MoinMoin.mail import sendmail +import subprocess +def _send_verification_mail(request, user): + _ = request.getText + querystr = {'action': 'verifyaccount', + 'i': user.id, + 'v': user.account_verification} + page = Page(request, "FrontPage") + pagelink = "%(link)s" % {'link': request.getQualifiedURL(page.url(request, querystr))} + subject = _('[%(sitename)s] account verification check for new user %(username)s') % { + 'sitename': request.page.cfg.sitename or request.url_root, + 'username': user.name, + } + + text = "Please verify your account by visiting this URL:\n\n %(link)s\n\n" % { + 'link': pagelink} + + mailok, msg = sendmail.sendmail(request, user.email, subject, text, request.cfg.mail_from) + if mailok: + return (1, _("Verification message sent to %(email)s" % {'email': user.email})) + else: + return (mailok, msg) def _create_user(request): @@ -43,6 +65,16 @@ # Name required to be unique. Check if name belong to another user. - if user.getUserId(request, theuser.name): - return _("This user name already belongs to somebody else.") + userid = user.getUserId(request, theuser.name) + if userid: + if request.cfg.require_email_verification and theuser.account_verification: + resendlink = request.page.url(request, querystr={ + 'action': 'newaccount', + 'i': userid, + 'resend': '1'}) + return _('This user name already belongs to somebody else. If this is a new account' + ' and you need another verification link, try ' + 'sending another one. ' % resendlink) + else: + return _("This user name already belongs to somebody else.") # try to get the password and pw repeat @@ -73,16 +105,39 @@ theuser.email = email.strip() if not theuser.email and 'email' not in request.cfg.user_form_remove: - return _("Please provide your email address. If you lose your" - " login information, you can get it by email.") + if request.cfg.require_email_verification: + return _("Please provide your email address. You will need it" + " to be able to confirm your registration.") + else: + return _("Please provide your email address. If you lose your" + " login information, you can get it by email.") # Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py if theuser.email and request.cfg.user_email_unique: - if user.get_by_email_address(request, theuser.email): - return _("This email already belongs to somebody else.") + emailuser = user.get_by_email_address(request, theuser.email) + if emailuser: + if request.cfg.require_email_verification and theuser.account_verification: + resendlink = request.page.url(request, querystr={ + 'action': 'newaccount', + 'i': emailuser.id, + 'resend': '1'}) + return _('This email already belongs to somebody else. If this is a new account' + ' and you need another verification link, try ' + 'sending another one. ' % resendlink) + else: + return _("This email already belongs to somebody else.") + + # Send verification links if desired + if request.cfg.require_email_verification: + mailok, msg = _send_verification_mail(request, theuser) + if mailok: + result = _("User account created! Use the link in your email (%s) to verify your account" + " then you will be able to use this account to login..." % theuser.email) + else: + request.theme.add_msg(_("Unable to send verification mail, %s. Account creation aborted." % msg), "error") + else: + result = _("User account created! You can use this account to login now...") # save data theuser.save() - - result = _("User account created! You can use this account to login now...") return result @@ -171,7 +226,18 @@ submitted = form.has_key('create') + uid = request.values.get('i', None) + resend = request.values.get('resend', None) + if submitted: # user pressed create button request.theme.add_msg(_create_user(request), "dialog") return page.send_page() + if resend and uid: + theuser = user.User(request, id=uid) + mailok, msg = _send_verification_mail(request, theuser) + if mailok: + request.theme.add_msg(_("Verification message re-sent to %s" % theuser.email), "dialog") + else: + request.theme.add_msg(_("Unable to re-send verification message, %s" % msg), "dialog") + return page.send_page() else: # show create form request.theme.send_title(_("Create Account"), pagename=pagename) --- ./MoinMoin/action/verifyaccount.py 1969-12-31 16:00:00.000000000 -0800 +++ ./MoinMoin/action/verifyaccount.py 2016-01-15 13:53:33.209957000 -0800 @@ -0,0 +1,64 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - verify account action + + @copyright: 2012 Steve McIntyre + @license: GNU GPL, see COPYING for details. +""" + +from MoinMoin import user, wikiutil +from MoinMoin.Page import Page +from MoinMoin.widget import html +from MoinMoin.auth import MoinAuth + +def execute(pagename, request): + found = False + for auth in request.cfg.auth: + if isinstance(auth, MoinAuth): + found = True + break + + if not found: + # we will not have linked, so forbid access + request.makeForbidden(403, 'No MoinAuth in auth list') + return + + page = Page(request, "FrontPage") + _ = request.getText + + if not request.cfg.require_email_verification: + result = _("Verification not configured!") + request.theme.add_msg(result, "error") + return page.send_page() + + uid = request.values.get('i', None) + verify = request.values.get('v', None) + + # Grab user profile + theuser = user.User(request, id=uid) + + # Compare the verification code + if not theuser.valid: + result = _("Unable to verify user account i=%s v=%s") % (uid, verify) + request.theme.add_msg(result, "error") + return page.send_page() + + if not theuser.account_verification: + result = _("User account has been verified!") + request.theme.add_msg(result, "error") + return page.send_page() + + if theuser.account_verification != verify: + result = _("Unable to verify user account i=%s v=%s") % (uid, verify) + request.theme.add_msg(result, "error") + return page.send_page() + + # All looks sane. Mark verification as done, save data + theuser.account_verification = "" + theuser.save() + + loginlink = request.page.url(request, querystr={'action': 'login'}) + result = _('User account verified! You can use this account to login now...' % loginlink) + request.theme.add_msg(result, "dialog") + return page.send_page() + --- ./MoinMoin/auth/__init__.py 2014-10-17 12:45:32.000000000 -0700 +++ ./MoinMoin/auth/__init__.py 2016-01-15 13:53:33.210285000 -0800 @@ -250,6 +250,13 @@ check_surge_protect(request, action='auth-name', username=username) - u = user.User(request, name=username, password=password, auth_method=self.name) + u = user.User(request, name=username, password=password, auth_method=self.name) if u.valid: + try: + verification = u.account_verification + except: + verification = False + if request.cfg.require_email_verification and verification: + logging.debug("%s: could not authenticate user %r (not verified yet)" % (self.name, username)) + return ContinueLogin(user_obj, _("User account not verified yet.")) logging.debug("%s: successfully authenticated user %r (valid)" % (self.name, u.name)) log_attempt("auth/login (moin)", True, request, username) --- ./MoinMoin/config/multiconfig.py 2014-10-17 12:45:32.000000000 -0700 +++ ./MoinMoin/config/multiconfig.py 2016-01-15 13:53:33.210918000 -0800 @@ -1097,4 +1097,6 @@ ('userprefs_disabled', [], "Disable the listed user preferences plugins."), + ('require_email_verification', False , + "Require verification of new user accounts."), )), # ========================================================================== --- ./MoinMoin/user.py 2014-10-17 12:45:32.000000000 -0700 +++ ./MoinMoin/user.py 2016-01-15 13:53:33.211435000 -0800 @@ -24,4 +24,5 @@ from copy import deepcopy import md5crypt +import uuid try: @@ -523,4 +524,10 @@ if password is not None: self.enc_password = encodePassword(self._cfg, password) + self.account_creation_date = str(time.time()) + self.account_creation_host = self._request.remote_addr + if self._cfg.require_email_verification: + self.account_verification = uuid.uuid4() + else: + self.account_verification = "" # "may" so we can say "if user.may.read(pagename):"