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') 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 assembl_login_complete_view(request): """ This backend view handles login form submissions received from both v1 and v2 frontend views. Check if proper authorization. Otherwise send to another page. """ session = AgentProfile.default_db # POST before GET identifier = (request.POST.get('identifier').strip() or request.GET.get('identifier').strip() or '') password = request.params.get('password', '').strip() referrer = request.POST.get('referrer', None) is_v2 = True if referrer == 'v2' else False next_view = handle_next_view(request, True) logged_in = request.authenticated_userid localizer = request.localizer user = None user, account = from_identifier(identifier) query = { "identifier": identifier, "next": next_view } if identifier else { "next": next_view } if not user: error_message = localizer.translate(_("This user cannot be found")) request.session.flash(error_message) route_name = 'react_login' if is_v2 else 'login' return HTTPFound( location=maybe_contextual_route(request, route_name, _query=query)) if account and not account.verified: return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=account.id)) if logged_in: if user.id != logged_in: # logging in as a different user # Could I be combining account? forget(request) else: # re-logging in? Why? return HTTPFound(location=next_view) if not user.check_password(password): error_message = localizer.translate(_("Invalid user and password")) user.login_failures += 1 # TODO: handle high failure count request.session.flash(error_message) route_name = 'react_login' if is_v2 else 'login' return HTTPFound( location=maybe_contextual_route(request, route_name, _query=query)) user.successful_login() headers = remember(request, user.id) request.response.headerlist.extend(headers) discussion = discussion_from_request(request) if discussion: maybe_auto_subscribe(user, discussion) return HTTPFound(location=next_view)
def assembl_login_complete_view(request): """ This backend view handles login form submissions received from both v1 and v2 frontend views. Check if proper authorization. Otherwise send to another page. """ session = AgentProfile.default_db # POST before GET identifier = (request.POST.get('identifier').strip() or request.GET.get('identifier').strip() or '') password = request.params.get('password', '').strip() referrer = request.POST.get('referrer', None) is_v2 = True if referrer == 'v2' else False next_view = handle_next_view(request, True) logged_in = request.authenticated_userid localizer = request.localizer user = None user, account = from_identifier(identifier) query = {"identifier": identifier, "next": next_view} if identifier else {"next": next_view} if not user: error_message = localizer.translate(_("This user cannot be found")) request.session.flash(error_message) route_name = 'react_login' if is_v2 else 'login' return HTTPFound(location=maybe_contextual_route( request, route_name, _query=query)) if account and not account.verified: return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=account.id)) if logged_in: if user.id != logged_in: # logging in as a different user # Could I be combining account? forget(request) else: # re-logging in? Why? return HTTPFound(location=next_view) if not user.check_password(password): error_message = localizer.translate(_("Invalid user and password")) user.login_failures += 1 # TODO: handle high failure count request.session.flash(error_message) route_name = 'react_login' if is_v2 else 'login' return HTTPFound(location=maybe_contextual_route( request, route_name, _query=query)) user.successful_login() headers = remember(request, user.id) request.response.headerlist.extend(headers) discussion = discussion_from_request(request) if discussion: maybe_auto_subscribe(user, discussion) return HTTPFound(location=next_view)
def assembl_login_complete_view(request): # Check if proper authorization. Otherwise send to another page. session = AgentProfile.default_db # POST before GET identifier = (request.POST.get('identifier').strip() or request.GET.get('identifier').strip() or '') password = request.params.get('password', '').strip() next_view = handle_next_view(request, True) logged_in = authenticated_userid(request) localizer = request.localizer user = None user, account = from_identifier(identifier) if not user: error_message = localizer.translate(_("This user cannot be found")) request.session.flash(error_message) return HTTPFound(location=maybe_contextual_route( request, 'login', _query={"identifier": identifier} if identifier else None)) if account and not account.verified: return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=account.id)) if logged_in: if user.id != logged_in: # logging in as a different user # Could I be combining account? forget(request) else: # re-logging in? Why? return HTTPFound(location=next_view) if not user.check_password(password): error_message = localizer.translate(_("Invalid user and password")) user.login_failures += 1 # TODO: handle high failure count request.session.flash(error_message) return HTTPFound(location=maybe_contextual_route( request, 'login', _query={"identifier": identifier} if identifier else None)) user.last_login = datetime.utcnow() headers = remember(request, user.id) request.response.headerlist.extend(headers) discussion = discussion_from_request(request) if discussion: maybe_auto_subscribe(user, discussion) return HTTPFound(location=next_view)
def assembl_login_complete_view(request): # Check if proper authorization. Otherwise send to another page. session = AgentProfile.default_db identifier = request.params.get('identifier', '').strip() password = request.params.get('password', '').strip() next_view = handle_next_view(request, True) logged_in = authenticated_userid(request) localizer = request.localizer user = None user, account = from_identifier(identifier) if not user: return dict(get_login_context(request), error=localizer.translate(_("This user cannot be found"))) if account and not account.verified: return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=account.id)) if logged_in: if user.id != logged_in: # logging in as a different user # Could I be combining account? forget(request) else: # re-logging in? Why? return HTTPFound(location=next_view) if not user.check_password(password): user.login_failures += 1 # TODO: handle high failure count session.add(user) return dict(get_login_context(request), error=localizer.translate(_("Invalid user and password"))) user.last_login = datetime.utcnow() headers = remember(request, user.id) request.response.headerlist.extend(headers) discussion = discussion_from_request(request) if discussion: maybe_auto_subscribe(user, discussion) return HTTPFound(location=next_view)
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 assembl_register_view(request): slug = request.matchdict.get('discussion_slug', "") next_view = handle_next_view(request) if not request.params.get('email'): if request.scheme == "http"\ and asbool(config.get("accept_secure_connection")): return HTTPFound(get_global_base_url(True) + request.path_qs) response = get_login_context(request) return response forget(request) session = AgentProfile.default_db localizer = request.localizer name = request.params.get('name', '').strip() if not name or len(name) < 3: return dict(get_default_context(request), error=localizer.translate(_( "Please use a name of at least 3 characters"))) password = request.params.get('password', '').strip() password2 = request.params.get('password2', '').strip() email = request.params.get('email', '').strip() if not is_email(email): return dict(get_default_context(request), error=localizer.translate(_( "This is not a valid email"))) email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email, verified=True).count(): return dict(get_default_context(request), error=localizer.translate(_( "We already have a user with this email."))) if password != password2: return dict(get_default_context(request), error=localizer.translate(_( "The passwords should be identical"))) # TODO: Validate password quality # otherwise create. validate_registration = asbool(config.get( 'assembl.validate_registration_emails')) user = User( name=name, password=password, verified=not validate_registration, creation_date=datetime.utcnow() ) email_account = EmailAccount( email=email, verified=not validate_registration, profile=user ) session.add(user) session.add(email_account) discussion = discussion_from_request(request) if discussion: permissions = get_permissions(Everyone, discussion.id) if not (P_SELF_REGISTER in permissions or P_SELF_REGISTER_REQUEST in permissions): discussion = None if discussion: _now = datetime.utcnow() agent_status = AgentStatusInDiscussion( agent_profile=user, discussion=discussion, first_visit=_now, last_visit=_now, user_created_on_this_discussion=True) session.add(agent_status) session.flush() if not validate_registration: if asbool(config.get('pyramid.debug_authorization')): # for debugging purposes from assembl.auth.password import email_token print "email token:", request.route_url( 'user_confirm_email', token=email_token(email_account)) headers = remember(request, user.id) user.last_login = datetime.utcnow() request.response.headerlist.extend(headers) if discussion: maybe_auto_subscribe(user, discussion) # TODO: Tell them to expect an email. return HTTPFound(location=next_view) return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=email_account.id))
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'))
def assembl_register_view(request): slug = request.matchdict.get('discussion_slug', "") p_slug = "/" + slug if slug else "" next_view = handle_next_view(request) if not request.params.get('email'): if request.scheme == "http"\ and asbool(config.get("accept_secure_connection")): raise HTTPFound("https://" + request.host + request.path_qs) response = dict(get_login_context(request), slug_prefix=p_slug) if request.GET.get('error', None): response['error'] = request.GET['error'] return response forget(request) session = AgentProfile.default_db localizer = request.localizer name = request.params.get('name', '').strip() if not name or len(name) < 3: return dict(get_default_context(request), slug_prefix=p_slug, error=localizer.translate(_( "Please use a name of at least 3 characters"))) password = request.params.get('password', '').strip() password2 = request.params.get('password2', '').strip() email = request.params.get('email', '').strip() if not is_email(email): return dict(get_default_context(request), slug_prefix=p_slug, error=localizer.translate(_( "This is not a valid email"))) email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email, verified=True).count(): return dict(get_default_context(request), slug_prefix=p_slug, error=localizer.translate(_( "We already have a user with this email."))) if password != password2: return dict(get_default_context(request), slug_prefix=p_slug, error=localizer.translate(_( "The passwords should be identical"))) # TODO: Validate password quality # otherwise create. validate_registration = asbool(config.get( 'assembl.validate_registration_emails')) user = User( name=name, password=password, verified=not validate_registration, creation_date=datetime.utcnow() ) email_account = EmailAccount( email=email, verified=not validate_registration, profile=user ) session.add(user) session.add(email_account) discussion = discussion_from_request(request) if discussion: _now = datetime.utcnow() agent_status = AgentStatusInDiscussion( agent_profile=user, discussion=discussion, first_visit=_now, last_visit=_now, user_created_on_this_discussion=True) session.add(agent_status) session.flush() if not validate_registration: if asbool(config.get('pyramid.debug_authorization')): # for debugging purposes from assembl.auth.password import email_token print "email token:", request.route_url( 'user_confirm_email', ticket=email_token(email_account)) headers = remember(request, user.id) user.last_login = datetime.utcnow() request.response.headerlist.extend(headers) if discussion: maybe_auto_subscribe(user, discussion) # TODO: Tell them to expect an email. return HTTPFound(location=next_view) return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=email_account.id))