def do_password_change(request): token = request.json_body.get('token') or '' password = request.json_body.get('password') or '' # TODO: Check password quality! localizer = request.localizer user, validity = verify_password_change_token(token) token_date = get_data_token_time(token) old_token = ( user is None or token_date is None or ( user.last_login and token_date < user.last_login)) if (validity != Validity.VALID or old_token): # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate(_( "This link is not valid. Do you want us to send another?")) else: error = localizer.translate(_( "This link has been used. Do you want us to send another?")) raise JSONError(error, validity) user.password_p = password user.last_login = datetime.utcnow() headers = remember(request, user.id) request.response.headerlist.extend(headers) return HTTPOk()
def do_password_change(request): token = request.json_body.get('token') or '' password1 = request.json_body.get('password1', '').strip() password2 = request.json_body.get('password2', '').strip() localizer = request.localizer if password1 is '' or password2 is '' or password2 != password1: error = localizer.translate( _("The passwords that were entered are mismatched!")) raise JSONError(error, ErrorTypes.PASSWORD) # TODO: Check password quality! user, validity = verify_password_change_token(token) token_date = get_data_token_time(token) old_token = (user is None or token_date is None or (user.last_login and token_date < user.last_login)) if (validity != Validity.VALID or old_token): # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate( _("This link is not valid. Do you want us to send another?")) else: error = localizer.translate( _("This link has been used. Do you want us to send another?")) raise JSONError(error, validity) user.password_p = password1 user.successful_login() headers = remember(request, user.id) request.response.headerlist.extend(headers) return HTTPOk()
def finish_password_change(request): localizer = request.localizer token = request.params.get('token') title = request.params.get('title') welcome = asbool(request.params.get('welcome')) discussion = discussion_from_request(request) if welcome: title = localizer.translate( _('Welcome to {discussion_topic}.')).format( discussion_topic=discussion.topic if discussion else "Assembl") else: title = localizer.translate(_('Change your password')) user, validity = verify_password_change_token(token) logged_in = request.authenticated_userid # if mismatch? if user and user.id != logged_in: # token for someone else: forget login. logged_in = None forget(request) token_date = get_data_token_time(token) old_token = (user is None or token_date is None or (user.last_login and token_date < user.last_login)) if (validity != Validity.VALID or old_token) and not logged_in: # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate( _("This link is not valid. Do you want us to send another?")) else: error = localizer.translate( _("This link has been used. Do you want us to send another?")) request.session.flash(error) return HTTPFound(location=maybe_contextual_route( request, 'request_password_change', _query=dict(user_id=user.id if user else ''))) error = None p1, p2 = (request.params.get('password1', '').strip(), request.params.get('password2', '').strip()) if p1 != p2: error = localizer.translate(_('The passwords are not identical')) elif p1: user.password_p = p1 user.successful_login() headers = remember(request, user.id) request.response.headerlist.extend(headers) if discussion: maybe_auto_subscribe(user, discussion) request.session.flash(localizer.translate(_("Password changed")), 'message') return HTTPFound(location=request.route_url( 'home' if discussion else 'discussion_list', discussion_slug=discussion.slug)) return dict(get_default_context(request), title=title, token=token, error=error)
def do_password_change(request): token = request.json_body.get('token') or '' password1 = request.json_body.get('password1', '').strip() password2 = request.json_body.get('password2', '').strip() localizer = request.localizer if password1 is '' or password2 is '' or password2 != password1: error = localizer.translate( _("The passwords that were entered are mismatched!")) raise JSONError(error, ErrorTypes.PASSWORD) # TODO: Check password quality! user, validity = verify_password_change_token(token) token_date = get_data_token_time(token) old_token = ( user is None or token_date is None or ( user.last_login and token_date < user.last_login)) if (validity != Validity.VALID or old_token): # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate(_( "This link is not valid. Do you want us to send another?")) else: error = localizer.translate(_( "This link has been used. Do you want us to send another?")) raise JSONError(error, validity) user.password_p = password1 user.successful_login() headers = remember(request, user.id) request.response.headerlist.extend(headers) return HTTPOk()
def finish_password_change(request): localizer = request.localizer token = request.params.get('token') title = request.params.get('title') welcome = asbool(request.params.get('welcome')) discussion = discussion_from_request(request) if welcome: title = localizer.translate(_( 'Welcome to {discussion_topic}.')).format( discussion_topic=discussion.topic if discussion else "Assembl") else: title = localizer.translate(_('Change your password')) user, validity = verify_password_change_token(token) logged_in = request.authenticated_userid # if mismatch? if user and user.id != logged_in: # token for someone else: forget login. logged_in = None forget(request) token_date = get_data_token_time(token) old_token = ( user is None or token_date is None or ( user.last_login and token_date < user.last_login)) if (validity != Validity.VALID or old_token) and not logged_in: # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate(_( "This link is not valid. Do you want us to send another?")) else: error = localizer.translate(_( "This link has been used. Do you want us to send another?")) request.session.flash(error) return HTTPFound(location=maybe_contextual_route( request, 'request_password_change', _query=dict( user_id=user.id if user else ''))) error = None p1, p2 = (request.params.get('password1', '').strip(), request.params.get('password2', '').strip()) if p1 != p2: error = localizer.translate(_('The passwords are not identical')) elif p1: user.password_p = p1 user.successful_login() headers = remember(request, user.id) request.response.headerlist.extend(headers) if discussion: maybe_auto_subscribe(user, discussion) request.session.flash(localizer.translate(_( "Password changed")), 'message') return HTTPFound(location=request.route_url( 'home' if discussion else 'discussion_list', discussion_slug=discussion.slug)) return dict( get_default_context(request), title=title, token=token, error=error)
def finish_password_change(request): localizer = request.localizer token = request.params.get('token') title = request.params.get('title') user, validity = verify_password_change_token(token) logged_in = authenticated_userid(request) # if mismatch? if user and user.id != logged_in: # token for someone else: forget login. logged_in = None forget(request) token_date = get_data_token_time(token) old_token = ( user is None or token_date is None or ( user.last_login and token_date < user.last_login)) if (validity != Validity.VALID or old_token) and not logged_in: # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate(_( "This link is not valid. Do you want us to send another?")) else: error = localizer.translate(_( "This link has been used. Do you want us to send another?")) return HTTPFound(location=maybe_contextual_route( request, 'request_password_change', _query=dict( user_id=user.id if user else '', error=error))) discussion_slug = request.matchdict.get('discussion_slug', None) error = None p1, p2 = (request.params.get('password1', '').strip(), request.params.get('password2', '').strip()) if p1 != p2: error = localizer.translate(_('The passwords are not identical')) elif p1: user.password_p = p1 user.last_login = datetime.utcnow() headers = remember(request, user.id) request.response.headerlist.extend(headers) if discussion_slug: discussion = discussion_from_request(request) maybe_auto_subscribe(user, discussion) return HTTPFound(location=request.route_url( 'home' if discussion_slug else 'discussion_list', discussion_slug=discussion_slug, _query=dict( message=localizer.translate(_( "Password changed"))))) slug_prefix = "/" + discussion_slug if discussion_slug else "" return dict( get_default_context(request), title=title, slug_prefix=slug_prefix, token=token, error=error)
def do_password_change(request): "Validate the change_password token, and react accordingly." # Codes below refer to those cases: # V. token Valid(+) or invalid(-)? (Possibly expired through internal date) # P. user has(+) a Password or not (-)? # W. Welcome(+) vs change password(-) # B. last login absent, or Before token created (+) vs last login after token created (-) # L. user is already Logged in(+) or not(-)? welcome = 'welcome' in request.matched_route.name localizer = request.localizer discussion = discussion_from_request(request) token = request.matchdict.get('token') user, validity = verify_password_change_token(token) logged_in = authenticated_userid(request) if user and user.id != logged_in: # token for someone else: forget login. logged_in = None forget(request) lacking_password = user is not None and user.password is None token_date = get_data_token_time(token) old_token = ( user is None or token_date is None or ( user.last_login and token_date < user.last_login)) print "pwc V%sP%sW%sB%sL%s" % tuple(map(lambda b: "-" if b else "+", ( validity != Validity.VALID, lacking_password, not welcome, old_token, logged_in is None))) if welcome and not lacking_password: # W+P+: welcome link sends onwards irrespective of token if logged_in: # L+: send onwards to discussion return HTTPFound(location=request.route_url( 'home' if discussion else 'discussion_list', discussion_slug=discussion.slug)) else: # L-: offer to login return HTTPFound(location=maybe_contextual_route( request, 'login', _query=dict( identifier=user.get_preferred_email() if user else None))) if (validity != Validity.VALID or old_token) and not logged_in: # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate(_( "This link is not valid. Do you want us to send another?")) else: error = localizer.translate(_( "This link has been used. Do you want us to send another?")) request.session.flash(error) return HTTPFound(location=maybe_contextual_route( request, 'request_password_change', _query=dict( user_id=user.id if user else ''))) # V+: Valid token (encompasses P-B+, W-, B-L+); ALSO V-L+ # V+P-B- should not happen, but we'll treat it the same. # go through password change dialog. We'll complete login afterwards. if welcome: if discussion: request.session.flash(localizer.translate(_( "You will enter the discussion as <b>{name}</b>.") ).format(name=user.name), 'message') else: request.session.flash(localizer.translate(_( "You will enter Assembl as <b>{name}</b>.") ).format(name=user.name), 'message') request.session.flash(localizer.translate(_( "Please choose your password for security reasons.") ).format(name=user.name), 'message') return HTTPFound(location=maybe_contextual_route( request, 'react_do_password_change', _query=dict( token=token, welcome=welcome)))
def user_confirm_email(request): token = request.matchdict.get('token') or '' account, validity = verify_email_token(token) session = AbstractAgentAccount.default_db logged_in = authenticated_userid(request) # if mismatch? localizer = request.localizer if account and account.profile_id != logged_in: # token for someone else: forget login. logged_in = None forget(request) token_date = get_data_token_time(token) old_token = ( account is None or token_date is None or ( account.profile.last_login and token_date < account.profile.last_login)) inferred_discussion = discussion = discussion_from_request(request) if account and not discussion: # We do not know from which discussion the user started to log in; # See if only involved in one discussion discussions = account.profile.involved_in_discussion if len(discussions) == 1: inferred_discussion = discussions[0] if account and account.verified and logged_in: # no need to revalidate, just send to discussion. # Question: maybe_auto_subscribe? Doubt it. if inferred_discussion: if inferred_discussion.preferences['landing_page']: route = 'new_home' else: route = 'home' else: route = 'discussion_list' error = localizer.translate( _("Email <%s> already confirmed")) % (account.email,) request.session.flash(error) return HTTPFound(location=request.route_url( route, discussion_slug=inferred_discussion.slug if inferred_discussion else None)) if validity != Validity.VALID or old_token: # V-, B-: Invalid or obsolete token # Offer to send a new token if account and not account.verified: # bad token, unverified account... offer a new token if validity != Validity.VALID: error = localizer.translate(_( "This link was not valid. We sent another.")) else: error = localizer.translate(_( "This link has been used. We sent another.")) request.session.flash(error) return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=account.id)) else: if account and account.verified: # bad token, verified account... send them to login error = localizer.translate( _("Email <%s> already confirmed")) % (account.email,) else: # now what? We do not have the email. # Just send to login for now error = localizer.translate(_( "This link is not valid. Please attempt to login to get another one.")) request.session.flash(error) return HTTPFound(location=maybe_contextual_route( request, 'react_login', _query=dict( identifier=account.email if account else None))) # By now we know we have a good token; make it login-equivalent. user = account.profile assert isinstance(user, User) # accounts should not get here. OK to fail. headers = remember(request, user.id) request.response.headerlist.extend(headers) user.last_login = datetime.utcnow() username = user.username.username if user.username else None next_view = handle_next_view(request, False) if account.verified: message = localizer.translate( _("Email <%s> already confirmed")) % (account.email,) else: # maybe another profile already verified that email other_account = session.query(AbstractAgentAccount).filter_by( email_ci=account.email_ci, verified=True).first() if other_account: # We have two versions of the email, delete the unverified one session.delete(account) if other_account.profile != user: # Give priority to the one where the email was verified last. other_profile = other_account.profile user.merge(other_profile) session.delete(other_profile) if user.username: username = user.username.username account = other_account account.verified = True user.verified = True # do not use inferred discussion for auto_subscribe user.last_login = datetime.utcnow() if discussion and maybe_auto_subscribe(user, discussion): message = localizer.translate(_( "Your email address %s has been confirmed, " "and you are now subscribed to discussion's " "default notifications.")) % (account.email,) else: message = localizer.translate(_( "Your email address %s has been confirmed." )) % (account.email,) if inferred_discussion: if inferred_discussion.preferences['landing_page']: route = 'new_home' else: route = 'home' else: route = 'discussion_list' return HTTPFound(location=request.route_url( route, discussion_slug=inferred_discussion.slug if inferred_discussion else None, _query=dict(message=message)))
def do_password_change(request): "Validate the change_password token, and react accordingly." # Codes below refer to those cases: # V. token Valid(+) or invalid(-)? (Possibly expired through internal date) # P. user has(+) a Password or not (-)? # W. Welcome(+) vs change password(-) # B. last login absent, or Before token created (+) vs last login after token created (-) # L. user is already Logged in(+) or not(-)? welcome = 'welcome' in request.matched_route.name localizer = request.localizer discussion = discussion_from_request(request) token = request.matchdict.get('ticket') user, validity = verify_password_change_token(token) logged_in = authenticated_userid(request) if user and user.id != logged_in: # token for someone else: forget login. logged_in = None forget(request) lacking_password = user is not None and user.password is None token_date = get_data_token_time(token) old_token = ( user is None or token_date is None or ( user.last_login and token_date < user.last_login)) print "pwc V%sP%sW%sB%sL%s" % tuple(map(lambda b: "-" if b else "+", ( validity != Validity.VALID, lacking_password, not welcome, old_token, logged_in is None))) if welcome and not lacking_password: # W+P+: welcome link sends onwards irrespective of token if logged_in: # L+: send onwards to discussion return HTTPFound(location=request.route_url( 'home' if discussion else 'discussion_list', discussion_slug=discussion.slug)) else: # L-: offer to login return HTTPFound(location=maybe_contextual_route( request, 'login', _query=dict( identifier=user.get_preferred_email() if user else None))) if (validity != Validity.VALID or old_token) and not logged_in: # V-, V+P+W-B-L-: Invalid or obsolete token (obsolete+logged in treated later.) # Offer to send a new token if validity != Validity.VALID: error = localizer.translate(_( "This link is not valid. Do you want us to send another?")) else: error = localizer.translate(_( "This link has been used. Do you want us to send another?")) return HTTPFound(location=maybe_contextual_route( request, 'request_password_change', _query=dict( user_id=user.id if user else '', error=error))) # V+: Valid token (encompasses P-B+, W-, B-L+); ALSO V-L+ # V+P-B- should not happen, but we'll treat it the same. # go through password change dialog. We'll complete login afterwards. slug = discussion.slug if discussion else "" slug_prefix = "/" + slug if slug else "" if welcome: if discussion: discussion_topic = discussion.topic welcome_text = localizer.translate(_( "You will enter the discussion as <b>{name}</b>.")).format(name=user.name) else: discussion_topic = "Assembl" welcome_text = localizer.translate(_( "You will enter Assembl as <b>{name}</b>.")) welcome_text += "</p><p>" + localizer.translate(_( "Please choose your password for security reasons.")) title = localizer.translate(_('Welcome to {discussion_topic}.')).format( discussion_topic=discussion_topic) else: title = localizer.translate(_('Change your password')) welcome_text = "" return dict( get_default_context(request), slug_prefix=slug_prefix, description=welcome_text, token=token, title=title)
def user_confirm_email(request): token = request.matchdict.get('ticket') account, validity = verify_email_token(token) session = AbstractAgentAccount.default_db logged_in = authenticated_userid(request) # if mismatch? localizer = request.localizer if account and account.profile_id != logged_in: # token for someone else: forget login. logged_in = None forget(request) token_date = get_data_token_time(token) old_token = ( account is None or token_date is None or ( account.profile.last_login and token_date < account.profile.last_login)) inferred_discussion = discussion = discussion_from_request(request) if account and not discussion: # We do not know from which discussion the user started to log in; # See if only involved in one discussion discussions = account.profile.involved_in_discussion if len(discussions) == 1: inferred_discussion = discussions[0] if account.verified and logged_in: # no need to revalidate, just send to discussion. # Question: maybe_auto_subscribe? Doubt it. return HTTPFound(location=request.route_url( 'home' if inferred_discussion else 'discussion_list', discussion_slug=inferred_discussion.slug, _query=dict(message=localizer.translate( _("Email <%s> already confirmed")) % (account.email,)))) if validity != Validity.VALID or old_token: # V-, B-: Invalid or obsolete token # Offer to send a new token if account and not account.verified: # bad token, unverified account... offer a new token if validity != Validity.VALID: error = localizer.translate(_( "This link was not valid. We sent another.")) else: error = localizer.translate(_( "This link has been used. We sent another.")) return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=account.id, _query=(dict(error=error)))) else: if account and account.verified: # bad token, verified account... send them to login error = localizer.translate( _("Email <%s> already confirmed")) % (account.email,) else: # now what? We do not have the email. # Just send to login for now error = localizer.translate(_( "This link is not valid. Please attempt to login to get another one." )) % (account.email,) return HTTPFound(location=maybe_contextual_route( request, 'login', _query=dict( identifier=account.email if account else None, message=error))) # By now we know we have a good token; make it login-equivalent. user = account.profile assert isinstance(user, User) # accounts should not get here. OK to fail. headers = remember(request, user.id) request.response.headerlist.extend(headers) user.last_login = datetime.utcnow() username = user.username.username if user.username else None next_view = handle_next_view(request, False) if account.verified: message = localizer.translate( _("Email <%s> already confirmed")) % (account.email,) else: # maybe another profile already verified that email other_account = session.query(AbstractAgentAccount).filter_by( email_ci=account.email_ci, verified=True).first() if other_account: # We have two versions of the email, delete the unverified one session.delete(account) if other_account.profile != user: # Give priority to the one where the email was verified last. other_profile = other_account.profile user.merge(other_profile) session.delete(other_profile) if user.username: username = user.username.username account = other_account account.verified = True user.verified = True # do not use inferred discussion for auto_subscribe user.last_login = datetime.utcnow() if discussion and maybe_auto_subscribe(user, discussion): message = localizer.translate(_( "Your email address %s has been confirmed, " "and you are now subscribed to discussion's " "default notifications.")) % (account.email,) else: message = localizer.translate(_( "Your email address %s has been confirmed." )) % (account.email,) if inferred_discussion: return HTTPFound(location=request.route_url( 'home', discussion_slug=inferred_discussion.slug, _query=dict(message=message))) else: return HTTPFound( location=request.route_url('discussion_list'))