def get_login_context(request, force_show_providers=False): slug = request.matchdict.get('discussion_slug', None) if slug: p_slug = "/" + slug request.session['discussion'] = slug else: p_slug = "" request.session.pop('discussion') providers = get_providers_with_names() discussion = discussion_from_request(request) hide_registration = (discussion and not public_roles.intersection(set(roles_with_permissions( discussion, P_READ))) and not roles_with_permissions( discussion, P_SELF_REGISTER_REQUEST, P_SELF_REGISTER)) if not force_show_providers: hide_providers = request.registry.settings.get( 'hide_login_providers', ()) if isinstance(hide_providers, (str, unicode)): hide_providers = (hide_providers, ) for provider in hide_providers: del providers[provider] return dict(get_default_context(request), slug_prefix=p_slug, providers=providers, hide_registration=hide_registration, identifier = request.params.get('identifier', ''), google_consumer_key=request.registry.settings.get( 'google.consumer_key', ''), next=handle_next_view(request))
def password_change_sent(request): localizer = request.localizer if not request.params.get('sent', False): profile_id = int(request.matchdict.get('profile_id')) profile = AgentProfile.get(profile_id) email = request.params.get('email') if not profile: raise HTTPNotFound("No profile " + str(profile_id)) else: email = email or profile.get_preferred_email() discussion = discussion_from_request(request) send_change_password_email(request, profile, email, discussion=discussion) profile_id = int(request.matchdict.get('profile_id')) context = get_default_context(request) return dict( context, profile_id=profile_id, action=context['get_route']("password_change_sent", profile_id=profile_id), error=request.params.get('error'), title=localizer.translate(_('Password change requested')), description=localizer.translate( _('We have sent you an email with a temporary connection link. ' 'Please use that link to log in and change your password.')))
def get_login_context(request, force_show_providers=False): slug = request.matchdict.get('discussion_slug', None) if slug: p_slug = "/" + slug request.session['discussion'] = slug else: p_slug = "" request.session.pop('discussion') providers = get_providers_with_names() discussion = discussion_from_request(request) hide_registration = (discussion and not public_roles.intersection(set(roles_with_permissions( discussion, P_READ))) and not roles_with_permissions( discussion, P_SELF_REGISTER_REQUEST, P_SELF_REGISTER)) if not force_show_providers: hide_providers = request.registry.settings.get( 'hide_login_providers', ()) if isinstance(hide_providers, (str, unicode)): hide_providers = (hide_providers, ) for provider in hide_providers: del providers[provider] return dict(get_default_context(request), slug_prefix=p_slug, providers=providers, saml_providers=request.registry.settings.get( 'SOCIAL_AUTH_SAML_ENABLED_IDPS', {}), hide_registration=hide_registration, identifier = request.params.get('identifier', ''), google_consumer_key=request.registry.settings.get( 'google.consumer_key', ''), next=handle_next_view(request))
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 get_social_autologin(request, discussion=None, next_view=None): """Look for a mandatory social login""" discussion = discussion or discussion_from_request(request) if discussion: preferences = discussion.preferences else: preferences = Preferences.get_default_preferences() auto_login_backend = preferences['authorization_server_backend'] landing_page = preferences['landing_page'] if not auto_login_backend: return None next_view = next_view or request.params.get('next', None) if discussion and not next_view: if landing_page: next_view = request.route_path('new_home', discussion_slug=discussion.slug) else: next_view = request.route_path('home', discussion_slug=discussion.slug) query = {"next": next_view} if ":" in auto_login_backend: auto_login_backend, provider = auto_login_backend.split(":", 1) query['idp'] = provider if discussion: return request.route_url( "contextual_social.auth", discussion_slug=discussion.slug, backend=auto_login_backend, _query=query) else: return request.route_url( "social.auth", backend=auto_login_backend, _query=query)
def get_login_context(request, force_show_providers=False): slug = request.matchdict.get('discussion_slug', None) if slug: request.session['discussion'] = slug else: request.session.pop('discussion') discussion = discussion_from_request(request) get_routes = create_get_route(request, discussion) providers = get_provider_data(get_routes) hide_registration = (discussion and not public_roles.intersection(set(roles_with_permissions( discussion, P_READ))) and not roles_with_permissions( discussion, P_SELF_REGISTER_REQUEST, P_SELF_REGISTER)) if not force_show_providers: hide_providers = aslist(request.registry.settings.get( 'hide_login_providers', ())) if isinstance(hide_providers, (str, unicode)): hide_providers = (hide_providers, ) providers = [p for p in providers if p['type'] not in hide_providers] return dict(get_default_context(request), providers=providers, providers_json=json.dumps(providers), saml_providers=request.registry.settings.get( 'SOCIAL_AUTH_SAML_ENABLED_IDPS', {}), hide_registration=hide_registration, identifier = request.params.get('identifier', ''), google_consumer_key=request.registry.settings.get( 'google.consumer_key', ''), next=handle_next_view(request), get_route=get_routes)
def create_get_route(request, discussion=0): if discussion is 0: # None would be a known absence, don't recalculate from assembl.auth.util import discussion_from_request discussion = discussion_from_request(request) if discussion: def get_route(name, **kwargs): if name == "bare_slug": name = "new_home" if discussion.preferences[ 'landing_page'] else "home" try: return request.route_path('contextual_' + name, discussion_slug=discussion.slug, **kwargs) except KeyError: return request.route_path(name, discussion_slug=discussion.slug, **kwargs) else: def get_route(name, **kwargs): kwargs['discussion_slug'] = kwargs.get('discussion_slug', '') return request.route_path(name, **kwargs) return get_route
def password_change_sent(request): localizer = request.localizer if not request.params.get('sent', False): profile_id = int(request.matchdict.get('profile_id')) profile = AgentProfile.get(profile_id) email = request.params.get('email') if not profile: raise HTTPNotFound("No profile "+str(profile_id)) else: email = email or profile.get_preferred_email() discussion = discussion_from_request(request) send_change_password_email(request, profile, email, discussion=discussion) slug = request.matchdict.get('discussion_slug', None) slug_prefix = "/" + slug if slug else "" profile_id=int(request.matchdict.get('profile_id')) return dict( get_default_context(request), profile_id=profile_id, slug_prefix=slug_prefix, action = "%s/password_change_sent/%d" % (slug_prefix, profile_id), error=request.params.get('error'), title=localizer.translate(_('Password change requested')), description=localizer.translate(_( 'We have sent you an email with a temporary connection link. ' 'Please use that link to log in and change your password.')))
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 maybe_contextual_route(request, route_name, **args): discussion_slug = None if request.matchdict: discussion_slug = request.matchdict.get('discussion_slug', None) if discussion_slug is None: discussion = discussion_from_request(request) if discussion: discussion_slug = discussion.slug if discussion_slug is None: return request.route_url(route_name, **args) else: return request.route_url( 'contextual_' + route_name, discussion_slug=discussion_slug, **args)
def maybe_contextual_route(request, route_name, **args): # TODO : Update this logic, as it uses session storage to identify slug as well, # it is called from places where session might not be a good idea. discussion_slug = None if request.matchdict: discussion_slug = request.matchdict.get('discussion_slug', None) if discussion_slug is None: discussion = discussion_from_request(request) if discussion: discussion_slug = discussion.slug if discussion_slug is None: return request.route_url(route_name, **args) else: return request.route_url( 'contextual_' + route_name, discussion_slug=discussion_slug, **args)
def locale_negotiator(request): settings = get_config() available = settings.get('available_languages').split() locale = (request.cookies.get('_LOCALE_', None) or request.params.get('_LOCALE_', None)) # TODO: Set User preference in this function. if not locale: from pyramid.security import authenticated_userid from assembl.auth.util import discussion_from_request from assembl.models import get_session_maker user_id = authenticated_userid(request) if user_id: prefs = get_preferred_languages(get_session_maker()(), user_id) for locale in prefs: if locale in available: break if '_' not in locale: locale = ensure_locale_has_country(locale) if locale and locale in available: break else: locale = None if locale is None: discussion = discussion_from_request(request) if discussion: for locale in discussion.discussion_locales: if locale in available: break if '_' not in locale: locale = ensure_locale_has_country(locale) if locale and locale in available: break else: locale = None if not locale: locale = to_posix_string(default_locale_negotiator(request)) if locale and locale not in available: locale_with_country = ensure_locale_has_country(locale) if locale_with_country: locale = locale_with_country if not locale: locale = to_posix_string( request.accept_language.best_match( available, settings.get('pyramid.default_locale_name', 'en'))) request._LOCALE_ = locale return locale
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 locale_negotiator(request): settings = get_config() available = settings.get('available_languages').split() locale = (request.cookies.get('_LOCALE_', None) or request.params.get('_LOCALE_', None)) # TODO: Set User preference in this function. if not locale: from pyramid.security import authenticated_userid from assembl.auth.util import discussion_from_request from assembl.models import get_session_maker user_id = authenticated_userid(request) if user_id: prefs = get_preferred_languages(get_session_maker()(), user_id) for locale in prefs: if locale in available: break if '_' not in locale: locale = ensure_locale_has_country(locale) if locale and locale in available: break else: locale = None if locale is None: discussion = discussion_from_request(request) if discussion: for locale in discussion.discussion_locales: if locale in available: break if '_' not in locale: locale = ensure_locale_has_country(locale) if locale and locale in available: break else: locale = None if not locale: locale = to_posix_format(default_locale_negotiator(request)) if locale and locale not in available: locale_with_country = ensure_locale_has_country(locale) if locale_with_country: locale = locale_with_country if not locale: locale = to_posix_format(request.accept_language.best_match( available, settings.get('pyramid.default_locale_name', 'en'))) request._LOCALE_ = locale return locale
def get_social_autologin(request, discussion=None, next_view=None): """Look for a mandatory social login :param discussion: The discussion object :param next_view: None|Boolean|string The potential next_view to be appended """ discussion = discussion or discussion_from_request(request) if discussion: preferences = discussion.preferences else: preferences = Preferences.get_default_preferences() auto_login_backend = preferences['authorization_server_backend'] landing_page = preferences['landing_page'] if not auto_login_backend: return None use_next_view = True if next_view is False: use_next_view = False next_view = sanitize_next_view(next_view or request.params.get('next', None)) if discussion and not next_view and use_next_view: if landing_page: next_view = request.route_path('new_home', discussion_slug=discussion.slug) else: next_view = request.route_path('home', discussion_slug=discussion.slug) if use_next_view: query = {"next": next_view} else: query = {} if ":" in auto_login_backend: auto_login_backend, provider = auto_login_backend.split(":", 1) query['idp'] = provider if discussion: return request.route_url( "contextual_social.auth", discussion_slug=discussion.slug, backend=auto_login_backend, _query=query) else: return request.route_url( "social.auth", backend=auto_login_backend, _query=query)
def get_social_autologin(request, discussion=None, next_view=None): """Look for a mandatory social login :param discussion: The discussion object :param next_view: None|Boolean|string The potential next_view to be appended """ discussion = discussion or discussion_from_request(request) if discussion: preferences = discussion.preferences else: preferences = Preferences.get_default_preferences() auto_login_backend = preferences['authorization_server_backend'] landing_page = preferences['landing_page'] if not auto_login_backend: return None use_next_view = True if next_view is False: use_next_view = False next_view = sanitize_next_view(next_view or request.params.get('next', None)) if discussion and not next_view and use_next_view: if landing_page: next_view = request.route_path('new_home', discussion_slug=discussion.slug) # TODO: This path should be removed else: next_view = request.route_path('home', discussion_slug=discussion.slug) if use_next_view: query = {"next": next_view} else: query = {} if ":" in auto_login_backend: auto_login_backend, provider = auto_login_backend.split(":", 1) query['idp'] = provider if discussion: return request.route_url("contextual_social.auth", discussion_slug=discussion.slug, backend=auto_login_backend, _query=query) else: return request.route_url("social.auth", backend=auto_login_backend, _query=query)
def create_get_route(request, discussion=0): if discussion is 0: # None would be a known absence, don't recalculate from assembl.auth.util import discussion_from_request discussion = discussion_from_request(request) from assembl.lib.frontend_urls import FrontendUrls if discussion: furl = FrontendUrls(discussion) def get_route(name, **kwargs): # If the resource is a furl_* route, check for front-end # routes first then return potential V2/V1 route # NOTE: `furl_` prefix MUST be used in this context in # order to avoid conflicts with Pyramid routes # This would NOT be true if only the FrontendUrl route is # used if 'furl_' in name: kwargs.update({'slug': discussion.slug}) route_name = name.split('furl_')[1] route = furl.get_frontend_url(route_name, **kwargs) if route is not None: return route if name == "bare_slug": name = "new_home" if discussion.preferences['landing_page'] \ else "home" try: return request.route_path('contextual_' + name, discussion_slug=discussion.slug, **kwargs) except KeyError: return request.route_path(name, discussion_slug=discussion.slug, **kwargs) else: def get_route(name, **kwargs): # Front-end routes not under a discussion context is already # back-end aware kwargs['discussion_slug'] = kwargs.get('discussion_slug', '') return request.route_path(name, **kwargs) return get_route
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, 'register') logged_in = authenticated_userid(request) localizer = request.localizer user = None user, account = from_identifier(identifier) if not user: request.session['next_view'] = next_view 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) request.session['discussion'] = discussion.slug return HTTPFound(location=next_view)
def create_get_route(request, discussion=0): if discussion is 0: # None would be a known absence, don't recalculate from assembl.auth.util import discussion_from_request discussion = discussion_from_request(request) from assembl.lib.frontend_urls import FrontendUrls if discussion: furl = FrontendUrls(discussion) def get_route(name, **kwargs): # If the resource is a furl_* route, check for front-end # routes first then return potential V2/V1 route # NOTE: `furl_` prefix MUST be used in this context in # order to avoid conflicts with Pyramid routes # This would NOT be true if only the FrontendUrl route is # used if 'furl_' in name: kwargs.update({'slug': discussion.slug}) route_name = name.split('furl_')[1] route = furl.get_frontend_url(route_name, **kwargs) if route is not None: return route if name == "bare_slug": name = "new_home" if discussion.preferences['landing_page'] \ else "home" try: return request.route_path('contextual_' + name, discussion_slug=discussion.slug, **kwargs) except KeyError: return request.route_path( name, discussion_slug=discussion.slug, **kwargs) else: def get_route(name, **kwargs): # Front-end routes not under a discussion context is already # back-end aware kwargs['discussion_slug'] = kwargs.get('discussion_slug', '') return request.route_path(name, **kwargs) return get_route
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_default_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() 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"))) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email=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, 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) # TODO: Tell them to expect an email. request.session.pop('next_view') 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('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('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 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 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.successful_login() 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 assembl_register_user(request): forget(request) localizer = request.localizer session = AgentProfile.default_db json = request.json logger = logging.getLogger() discussion = discussion_from_request(request) permissions = get_permissions( Everyone, discussion.id if discussion else None) name = json.get('real_name', '').strip() errors = JSONError() if not name or len(name) < 3: errors.add_error(localizer.translate(_( "Please use a name of at least 3 characters")), ErrorTypes.SHORT_NAME) password = json.get('password', '').strip() # TODO: Check password strength. maybe pwdmeter? email = None for account in json.get('accounts', ()): email = account.get('email', None) if not is_email(email): errors.add_error(localizer.translate(_( "This is not a valid email")), ErrorTypes.INVALID_EMAIL) continue email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email).count(): if not discussion.preferences['generic_errors']: errors.add_error(localizer.translate(_( "We already have a user with this email.")), ErrorTypes.EXISTING_EMAIL, HTTPConflict.code) else: errors.add_error(localizer.translate( generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error("[User creation]: We already have a user with this email %s" % email) if not email: errors.add_error(localizer.translate(_("No email.")), ErrorTypes.INVALID_EMAIL) username = json.get('username', None) if username: if session.query(Username).filter( func.lower(Username.username) == username.lower()).count(): if not discussion.preferences['generic_errors']: errors.add_error(localizer.translate(_( "We already have a user with this username.")), ErrorTypes.EXISTING_USERNAME, HTTPConflict.code) else: errors.add_error(localizer.translate( generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error("We already have a user with username %s" % username) if len(username) > 20: errors.add_error(localizer.translate(_( "The username must be less than 20 characters.")), ErrorTypes.USERNAME_TOO_LONG, HTTPBadRequest.code) if discussion: check_subscription = discussion.preferences['whitelist_on_register'] whitelist = discussion.preferences['require_email_domain'] if check_subscription and whitelist: status = discussion.check_email(email) if not status: admin_emails = discussion.get_admin_emails() num = len(admin_emails) errors.add_error( localizer.pluralize( _("Your email domain has not been approved for registration. Please contact ${emails} for support."), _("Your email domain has not been approved for registration. Please contact one of ${emails} for support."), num, mapping={'emails': ", ".join(admin_emails)} ) ) if errors: raise errors # This logic needs to be above the JSONError checks to ensure that whitelisting is applied # even if the discussion does not have a P_SELF_REGISTER on system.Everyone if discussion and not ( P_SELF_REGISTER in permissions or P_SELF_REGISTER_REQUEST in permissions): # Consider it without context discussion = None validate_registration = asbool(config.get( 'assembl.validate_registration_emails')) old_autoflush = session.autoflush session.autoflush = False try: now = datetime.utcnow() user = User( name=name, password=password, verified=not validate_registration, creation_date=now ) session.add(user) session.flush() user.update_from_json(json, user_id=user.id) account = user.accounts[0] email = account.email account.verified = not validate_registration if discussion: 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() # create the profile fields for custom fields for global_id, value in json.get('profileFields', {}).iteritems(): configurable_field_id = from_global_id(global_id)[1] configurable_field = AbstractConfigurableField.get(configurable_field_id) profile_field = ProfileField( agent_profile=user, configurable_field=configurable_field, discussion=configurable_field.discussion, value_data={ u'value': value } ) session.add(profile_field) session.flush() if validate_registration: send_confirmation_email(request, account) else: user.verified = True for account in user.accounts: account.verified = True user.successful_login() 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(account)) if discussion: check_subscription = discussion.preferences['whitelist_on_register'] maybe_auto_subscribe(user, discussion, check_authorization=check_subscription) session.flush() return CreationResponse(user, Everyone, permissions) finally: session.autoflush = old_autoflush
def velruse_login_complete_view(request): session = AgentProfile.db context = request.context velruse_profile = context.profile discussion = None slug = request.session.get('discussion', None) if not slug: discussion = discussion_from_request(request) if discussion: slug = discussion.slug if slug and not discussion: discussion = session.query(Discussion).filter_by( slug=slug).first() next_view = handle_next_view(request, True) logged_in = authenticated_userid(request) provider = get_identity_provider(request) # find or create IDP_Accounts idp_accounts = [] new_idp_accounts = [] velruse_accounts = velruse_profile['accounts'] old_autoflush = session.autoflush # sqla mislikes creating accounts before profiles, so delay session.autoflush = False for velruse_account in velruse_accounts: if 'userid' in velruse_account: idp_accounts.extend(session.query( IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], userid=velruse_account['userid']).all()) elif 'username' in velruse_account: idp_accounts.extend(session.query( IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], username=velruse_account['username']).all()) else: raise HTTPServerError() if idp_accounts: for idp_account in idp_accounts: idp_account.profile_info_json = velruse_profile else: idp_account = IdentityProviderAccount( provider=provider, profile_info_json=velruse_profile, domain=velruse_account.get('domain'), userid=velruse_account.get('userid'), username=velruse_account.get('username')) idp_accounts.append(idp_account) new_idp_accounts.append(idp_account) session.add(idp_account) # find AgentProfile profile = None user = None profiles = [a.profile for a in idp_accounts if a.profile] # Maybe we already have a profile based on email if idp_account.email and idp_account.verified: email = idp_account.email other_account = session.query(AbstractAgentAccount).filter_by( email=email, verified=True).first() if other_account and other_account.profile \ and other_account.profile not in profiles: profiles.append(other_account.profile) profiles = list(set(profiles)) # prefer profiles with verified users, then users, then oldest profiles profiles.sort(key=lambda p: ( not(isinstance(p, User) and p.verified), not isinstance(p, User), p.id)) if logged_in: # NOTE: Must make sure that login page not available when # logged in as another account. user = session.query(User).filter_by(id=logged_in).first() if user: if user in profiles: profiles.remove(user) profiles.insert(0, user) username = None if len(profiles): # first is presumably best profile = profiles.pop(0) while len(profiles): other = profiles.pop() # Multiple profiles. We need to combine them to one. profile.merge(other) session.delete(other) if isinstance(profile, User): if profile.username: username = profile.username.username profile.last_login = datetime.now() if not profile.name: profile.name = velruse_profile.get('displayName', None) else: # Create a new user profile = User( name=velruse_profile.get('displayName', ''), verified=True, last_login=datetime.now(), creation_date=datetime.now(), #timezone=velruse_profile['utcOffset'], # TODO: needs parsing ) session.add(profile) usernames = set((a['preferredUsername'] for a in velruse_accounts if 'preferredUsername' in a)) for u in usernames: if not session.query(Username).filter_by(username=u).count(): username = u break if username: session.add(Username(username=username, user=profile)) session.flush() if maybe_auto_subscribe(profile, discussion): next_view = "/%s/user/notifications" % (slug,) for idp_account in new_idp_accounts: idp_account.profile = profile # Now all accounts have a profile session.autoflush = old_autoflush email_accounts = {ea.email: ea for ea in profile.email_accounts} # There may be new emails in the accounts verified_email = None if 'verifiedEmail' in velruse_profile: verified_email = velruse_profile['verifiedEmail'] if verified_email in email_accounts and provider.trust_emails: email_account = email_accounts[verified_email] if email_account.preferred: idp_account.preferred = True email_account.delete() for email_d in velruse_profile.get('emails', []): if isinstance(email_d, dict): email = email_d['value'] if verified_email != email: # create an unverified email account. email = EmailAccount( email=email, profile=profile ) session.add(email) else: if email_d.get('preferred', False): # maybe TODO: make the idp_account preferred, # if no other account is preferred? pass # Note that if an IdP account stops claiming an email, it "leaks". session.flush() user_id = profile.id headers = remember(request, user_id, tokens=format_token(profile)) request.response.headerlist.extend(headers) # TODO: Store the OAuth etc. credentials. # Though that may be done by velruse? return HTTPFound(location=next_view)
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 velruse_login_complete_view(request): # TODO: Write tests. Situations are a combinatorics of the folloing: # 1. User action: # A. Social login # B. Logged in, add social account # C. Logged in, add email account (does not happen here) # 2. Is there an existing account with that email? # A. No. # B. The social account already exists # C. A valid email account # D. An invalid email account, sole account of profile # E. An invalid email account, but profile has other accounts # F. A social account from a different provider # 3. When we're logged in (1B, 1C), is the existing account # on the logged in profile or another? # 4. If gmail account, is it an enterprise account? session = AgentProfile.default_db context = request.context now = datetime.utcnow() velruse_profile = context.profile discussion = None slug = request.session.get('discussion', None) if not slug: discussion = discussion_from_request(request) if discussion: slug = discussion.slug if slug and not discussion: discussion = session.query(Discussion).filter_by(slug=slug).first() next_view = handle_next_view(request, True) logged_in = authenticated_userid(request) if logged_in: logged_in = User.get(logged_in) base_profile = logged_in provider = get_identity_provider(request) # find or create IDP_Accounts idp_accounts = [] new_idp_account = None velruse_accounts = velruse_profile['accounts'] old_autoflush = session.autoflush # sqla mislikes creating accounts before profiles, so delay session.autoflush = False # Search for this social account for velruse_account in velruse_accounts: if 'userid' in velruse_account: idp_accounts.extend( session.query(IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], userid=velruse_account['userid']).all()) elif 'username' in velruse_account: idp_accounts.extend( session.query(IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], username=velruse_account['username']).all()) else: log.error("account needs username or email" + velruse_account) raise HTTPServerError("account needs username or userid") trusted_emails = set() if idp_accounts: for idp_account in idp_accounts: idp_account.profile_info_json = velruse_profile if len(idp_accounts) > 1: log.warn("multiple idp_accounts:" + ','.join((a.id for a in idp_accounts)) + " for " + velruse_accounts) # We will just the last one from the loop for now. trusted_emails.update([ a.email for a in idp_accounts if a.provider.trust_emails and a.email ]) else: # Create it if not found idp_class = IdentityProviderAccount for cls in idp_class.get_subclasses(): if cls == idp_class: continue if cls.account_provider_name == provider.name: idp_class = cls break idp_account = idp_class(provider=provider, profile_info_json=velruse_profile, domain=velruse_account.get('domain'), userid=velruse_account.get('userid'), verified=True, username=velruse_account.get('username')) idp_accounts.append(idp_account) new_idp_account = base_account = idp_account session.add(idp_account) for account in idp_accounts: if account.provider.trust_emails: profile = (velruse_profile if account == new_idp_account else account.profile_info_json) email = profile.get('verifiedEmail', None) if email: if account == new_idp_account: account.email = email trusted_emails.add(email) for email in profile.get('emails', ()): if isinstance(email, dict): email = email.get('value', None) if isinstance(email, (str, unicode)) and email: trusted_emails.add(email) if account == new_idp_account and not account.email: account.email = email # else we have created an email-less account. Treat accordingly. conflicting_profiles = {a.profile for a in idp_accounts if a.profile} # Are there other accounts/profiles than the ones we know? conflicting_accounts = set() for email in trusted_emails: other_accounts = session.query(AbstractAgentAccount).filter_by( email=email) for account in other_accounts: conflicting_accounts.add(account) if account.verified or len(account.profile.accounts) == 1: conflicting_profiles.add(account.profile) # choose best known profile for base_account # prefer profiles with verified users, then users, then oldest profiles profile_list = list(conflicting_profiles) profile_list.sort(key=lambda p: (not (isinstance(p, User) and p.verified), not isinstance(p, User), p.id)) if new_idp_account and profile_list: base_profile = profile_list[0] elif not new_idp_account: # Take first appropriate. Should be the first, somehow not. for profile in profile_list: accounts = [a for a in idp_accounts if a.profile == profile] if accounts: base_account = accounts[0] break if not logged_in: base_profile = base_account.profile new_profile = None if new_idp_account: if not base_profile: # Create a new user base_profile = new_profile = User(name=velruse_profile.get( 'displayName', ''), verified=True, creation_date=now) session.add(new_profile) # Upgrade a AgentProfile if not isinstance(base_profile, User): base_profile = base_profile.change_class(User, None, verified=True) new_idp_account.profile = base_profile base_profile.last_login = now base_profile.verified = True if not base_profile.name: base_profile.name = velruse_profile.get('displayName', None) # TODO (needs parsing) # base_profile.timezone=velruse_profile['utcOffset'] # Now all accounts have a profile session.autoflush = old_autoflush session.flush() # Merge other profiles with the same (validated) email # TODO: Ask the user about doing this. conflicting_profiles.discard(base_profile) conflicting_accounts.discard(base_account) for conflicting_profile in conflicting_profiles: base_profile.merge(conflicting_profile) session.delete(conflicting_profile) # If base_profile is still an AgentProfile at this point # then it needs to be upgraded to a User if not isinstance(base_profile, User): base_profile = base_profile.change_class(User, None, verified=True, last_login=now) # Set username if not base_profile.username: username = None usernames = set((a['preferredUsername'] for a in velruse_accounts if 'preferredUsername' in a)) for u in usernames: if not session.query(Username).filter_by(username=u).count(): username = u break if username: session.add(Username(username=username, user=base_profile)) # Create AgentStatusInDiscussion if new_profile and discussion: agent_status = AgentStatusInDiscussion( agent_profile=base_profile, discussion=discussion, user_created_on_this_discussion=True) session.add(agent_status) if maybe_auto_subscribe(base_profile, discussion): next_view = "/%s/" % (slug, ) # Delete other (email) accounts if base_account.provider.trust_emails: for account in conflicting_accounts: # Merge may have been confusing session.expire(account) account = AbstractAgentAccount.get(account.id) if account.profile == base_profile: if account.email == base_account.email: if isinstance(account, EmailAccount): account.delete() if account.verified and account.preferred: base_account.preferred = True elif isinstance(account, IdentityProviderAccount): if account.provider_id == base_account.provider_id: log.error("This should have been caught earlier") account.delete() else: log.warning("Two accounts with same email," + "different provider: %d, %d" % (account.id, base_account.id)) else: # If they're verified, they should have been merged. if account.verified: log.error("account %d should not exist: " % (account.id, )) else: other_profile = account.profile account.delete() session.flush() session.expire(other_profile, ["accounts"]) if not len(other_profile.accounts): log.warning("deleting profile %d" % other_profile.id) other_profile.delete() session.expire(base_profile, ['accounts', 'email_accounts']) # create an email account for other emails. known_emails = {a.email for a in base_profile.accounts} for email in trusted_emails: if email not in known_emails: email = EmailAccount(email=email, profile=base_profile, verified=base_account.provider.trust_emails) session.add(email) session.flush() base_profile.last_login = now headers = remember(request, base_profile.id) request.response.headerlist.extend(headers) if discussion: request.session['discussion'] = discussion.slug return HTTPFound(location=next_view)
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_default_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() 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"))) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by(email=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, 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) # TODO: Tell them to expect an email. request.session.pop('next_view') return HTTPFound(location=next_view) return HTTPFound(location=maybe_contextual_route( request, 'confirm_emailid_sent', email_account_id=email_account.id))
def assembl_register_user(request): forget(request) localizer = request.localizer session = AgentProfile.default_db json = request.json logger = logging.getLogger() discussion = discussion_from_request(request) permissions = get_permissions(Everyone, discussion.id if discussion else None) name = json.get('real_name', '').strip() errors = JSONError() if not name or len(name) < 3: errors.add_error( localizer.translate( _("Please use a name of at least 3 characters")), ErrorTypes.SHORT_NAME) password = json.get('password', '').strip() # TODO: Check password strength. maybe pwdmeter? email = None for account in json.get('accounts', ()): email = account.get('email', None) if not is_email(email): errors.add_error( localizer.translate(_("This is not a valid email")), ErrorTypes.INVALID_EMAIL) continue email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email).count(): if not discussion.preferences['generic_errors']: errors.add_error( localizer.translate( _("We already have a user with this email.")), ErrorTypes.EXISTING_EMAIL, HTTPConflict.code) else: errors.add_error(localizer.translate(generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error( "[User creation]: We already have a user with this email %s" % email) if not email: errors.add_error(localizer.translate(_("No email.")), ErrorTypes.INVALID_EMAIL) username = json.get('username', None) if username: if session.query(Username).filter( func.lower(Username.username) == username.lower()).count(): if not discussion.preferences['generic_errors']: errors.add_error( localizer.translate( _("We already have a user with this username.")), ErrorTypes.EXISTING_USERNAME, HTTPConflict.code) else: errors.add_error(localizer.translate(generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error("We already have a user with username %s" % username) if len(username) > 20: errors.add_error( localizer.translate( _("The username must be less than 20 characters.")), ErrorTypes.USERNAME_TOO_LONG, HTTPBadRequest.code) if discussion: check_subscription = discussion.preferences['whitelist_on_register'] whitelist = discussion.preferences['require_email_domain'] if check_subscription and whitelist: status = discussion.check_email(email) if not status: admin_emails = discussion.get_admin_emails() num = len(admin_emails) errors.add_error( localizer.pluralize( _("Your email domain has not been approved for registration. Please contact ${emails} for support." ), _("Your email domain has not been approved for registration. Please contact one of ${emails} for support." ), num, mapping={'emails': ", ".join(admin_emails)})) if errors: raise errors # This logic needs to be above the JSONError checks to ensure that whitelisting is applied # even if the discussion does not have a P_SELF_REGISTER on system.Everyone if discussion and not (P_SELF_REGISTER in permissions or P_SELF_REGISTER_REQUEST in permissions): # Consider it without context discussion = None validate_registration = asbool( config.get('assembl.validate_registration_emails')) old_autoflush = session.autoflush session.autoflush = False try: now = datetime.utcnow() user = User(name=name, password=password, verified=not validate_registration, creation_date=now) session.add(user) session.flush() user.update_from_json(json, user_id=user.id) account = user.accounts[0] email = account.email account.verified = not validate_registration if discussion: 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() # create the profile fields for custom fields for global_id, value in json.get('profileFields', {}).iteritems(): configurable_field_id = from_global_id(global_id)[1] configurable_field = AbstractConfigurableField.get( configurable_field_id) profile_field = ProfileField( agent_profile=user, configurable_field=configurable_field, discussion=configurable_field.discussion, value_data={u'value': value}) session.add(profile_field) session.flush() if validate_registration: send_confirmation_email(request, account) else: user.verified = True for account in user.accounts: account.verified = True user.successful_login() 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(account)) if discussion: check_subscription = discussion.preferences[ 'whitelist_on_register'] maybe_auto_subscribe(user, discussion, check_authorization=check_subscription) session.flush() return CreationResponse(user, Everyone, permissions) finally: session.autoflush = old_autoflush
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 velruse_login_complete_view(request): # TODO: Write tests. Situations are a combinatorics of the folloing: # 1. User action: # A. Social login # B. Logged in, add social account # C. Logged in, add email account (does not happen here) # 2. Is there an existing account with that email? # A. No. # B. The social account already exists # C. A valid email account # D. An invalid email account, sole account of profile # E. An invalid email account, but profile has other accounts # F. A social account from a different provider # 3. When we're logged in (1B, 1C), is the existing account # on the logged in profile or another? # 4. If gmail account, is it an enterprise account? session = AgentProfile.default_db context = request.context now = datetime.utcnow() velruse_profile = context.profile discussion = None slug = request.session.get('discussion', None) if not slug: discussion = discussion_from_request(request) if discussion: slug = discussion.slug if slug and not discussion: discussion = session.query(Discussion).filter_by( slug=slug).first() next_view = handle_next_view(request, True) logged_in = authenticated_userid(request) if logged_in: logged_in = User.get(logged_in) base_profile = logged_in provider = get_identity_provider(request) # find or create IDP_Accounts idp_accounts = [] new_idp_account = None velruse_accounts = velruse_profile['accounts'] old_autoflush = session.autoflush # sqla mislikes creating accounts before profiles, so delay session.autoflush = False # Search for this social account for velruse_account in velruse_accounts: if 'userid' in velruse_account: idp_accounts.extend(session.query( IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], userid=velruse_account['userid']).all()) elif 'username' in velruse_account: idp_accounts.extend(session.query( IdentityProviderAccount).filter_by( provider=provider, domain=velruse_account['domain'], username=velruse_account['username']).all()) else: log.error("account needs username or email" + velruse_account) raise HTTPServerError("account needs username or userid") trusted_emails = set() if idp_accounts: for idp_account in idp_accounts: idp_account.profile_info_json = velruse_profile if len(idp_accounts) > 1: log.warn("multiple idp_accounts:" + ','.join((a.id for a in idp_accounts)) + " for " + velruse_accounts) # We will just the last one from the loop for now. trusted_emails.update([ a.email for a in idp_accounts if a.provider.trust_emails and a.email]) else: # Create it if not found idp_class = IdentityProviderAccount for cls in idp_class.get_subclasses(): if cls == idp_class: continue if cls.account_provider_name == provider.name: idp_class = cls break idp_account = idp_class( provider=provider, profile_info_json=velruse_profile, domain=velruse_account.get('domain'), userid=velruse_account.get('userid'), verified=True, username=velruse_account.get('username')) idp_accounts.append(idp_account) new_idp_account = base_account = idp_account session.add(idp_account) for account in idp_accounts: if account.provider.trust_emails: profile = (velruse_profile if account == new_idp_account else account.profile_info_json) email = profile.get('verifiedEmail', None) if email: if account == new_idp_account: account.email = email trusted_emails.add(email) for email in profile.get('emails', ()): if isinstance(email, dict): email = email.get('value', None) if isinstance(email, (str, unicode)) and email: trusted_emails.add(email) if account == new_idp_account and not account.email: account.email = email # else we have created an email-less account. Treat accordingly. conflicting_profiles = {a.profile for a in idp_accounts if a.profile} # Are there other accounts/profiles than the ones we know? conflicting_accounts = set() for email in trusted_emails: other_accounts = session.query(AbstractAgentAccount).filter_by( email=email) for account in other_accounts: conflicting_accounts.add(account) if account.verified or len(account.profile.accounts) == 1: conflicting_profiles.add(account.profile) # choose best known profile for base_account # prefer profiles with verified users, then users, then oldest profiles profile_list = list(conflicting_profiles) profile_list.sort(key=lambda p: ( not(isinstance(p, User) and p.verified), not isinstance(p, User), p.id)) if new_idp_account and profile_list: base_profile = profile_list[0] elif not new_idp_account: # Take first appropriate. Should be the first, somehow not. for profile in profile_list: accounts = [ a for a in idp_accounts if a.profile == profile] if accounts: base_account = accounts[0] break if not logged_in: base_profile = base_account.profile new_profile = None if new_idp_account: if not base_profile: # Create a new user base_profile = new_profile = User( name=velruse_profile.get('displayName', ''), verified=True, creation_date=now) session.add(new_profile) # Upgrade a AgentProfile if not isinstance(base_profile, User): base_profile = base_profile.change_class( User, None, verified=True) new_idp_account.profile = base_profile base_profile.last_login = now base_profile.verified = True if not base_profile.name: base_profile.name = velruse_profile.get('displayName', None) # TODO (needs parsing) # base_profile.timezone=velruse_profile['utcOffset'] # Now all accounts have a profile session.autoflush = old_autoflush session.flush() # Merge other profiles with the same (validated) email # TODO: Ask the user about doing this. conflicting_profiles.discard(base_profile) conflicting_accounts.discard(base_account) for conflicting_profile in conflicting_profiles: base_profile.merge(conflicting_profile) session.delete(conflicting_profile) # If base_profile is still an AgentProfile at this point # then it needs to be upgraded to a User if not isinstance(base_profile, User): base_profile = base_profile.change_class( User, None, verified=True, last_login=now) # Set username if not base_profile.username: username = None usernames = set((a['preferredUsername'] for a in velruse_accounts if 'preferredUsername' in a)) for u in usernames: if not session.query(Username).filter_by(username=u).count(): username = u break if username: session.add(Username(username=username, user=base_profile)) # Create AgentStatusInDiscussion if new_profile and discussion: agent_status = AgentStatusInDiscussion( agent_profile=base_profile, discussion=discussion, user_created_on_this_discussion=True) session.add(agent_status) if maybe_auto_subscribe(base_profile, discussion): next_view = "/%s/" % (slug,) # Delete other (email) accounts if base_account.provider.trust_emails: for account in conflicting_accounts: # Merge may have been confusing session.expire(account) account = AbstractAgentAccount.get(account.id) if account.profile == base_profile: if account.email == base_account.email: if isinstance(account, EmailAccount): account.delete() if account.verified and account.preferred: base_account.preferred = True elif isinstance(account, IdentityProviderAccount): if account.provider_id == base_account.provider_id: log.error("This should have been caught earlier") account.delete() else: log.warning("Two accounts with same email," + "different provider: %d, %d" % ( account.id, base_account.id)) else: # If they're verified, they should have been merged. if account.verified: log.error("account %d should not exist: " % (account.id,)) else: other_profile = account.profile account.delete() session.flush() session.expire(other_profile, ["accounts"]) if not len(other_profile.accounts): log.warning("deleting profile %d" % other_profile.id) other_profile.delete() session.expire(base_profile, ['accounts', 'email_accounts']) # create an email account for other emails. known_emails = {a.email for a in base_profile.accounts} for email in trusted_emails: if email not in known_emails: email = EmailAccount( email=email, profile=base_profile, verified=base_account.provider.trust_emails ) session.add(email) session.flush() base_profile.last_login = now headers = remember(request, base_profile.id) request.response.headerlist.extend(headers) if discussion: request.session['discussion'] = discussion.slug return HTTPFound(location=next_view)
def assembl_register_user(request): forget(request) localizer = request.localizer session = AgentProfile.default_db json = request.json discussion = discussion_from_request(request) permissions = ctx.get_permissions() name = json.get('real_name', '').strip() errors = JSONError() if not name or len(name) < 3: errors.add_error(localizer.translate(_( "Please use a name of at least 3 characters")), ErrorTypes.SHORT_NAME) password = json.get('password', '').strip() # TODO: Check password strength. maybe pwdmeter? email = None for account in json.get('accounts', ()): email = account.get('email', None) if not is_email(email): errors.add_error(localizer.translate(_( "This is not a valid email")), ErrorTypes.INVALID_EMAIL) continue email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email, verified=True).count(): errors.add_error(localizer.translate(_( "We already have a user with this email.")), ErrorTypes.EXISTING_EMAIL, HTTPConflict.code) if not email: errors.add_error(localizer.translate(_("No email.")), ErrorTypes.INVALID_EMAIL) username = json.get('username', None) if username: if session.query(User).filter_by( username=username).count(): errors.add_error(localizer.translate(_( "We already have a user with this username.")), ErrorTypes.EXISTING_USERNAME, HTTPConflict.code) if errors: raise errors validate_registration = asbool(settings.get( 'assembl.validate_registration_emails')) old_autoflush = session.autoflush session.autoflush = False try: now = datetime.utcnow() user = User( name=name, password=password, verified=not validate_registration, creation_date=now ) session.add(user) session.flush() user.update_from_json(json, user_id=user.id) if discussion and not ( P_SELF_REGISTER in permissions or P_SELF_REGISTER_REQUEST in permissions): # Consider it without context discussion = None if discussion: 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() account = user.accounts[0] email = account.email account.verified = not validate_registration if validate_registration: send_confirmation_email(request, account) else: user.verified = True for account in user.accounts: account.verified = True if asbool(settings.get('pyramid.debug_authorization')): # for debugging purposes from assembl.auth.password import email_token log.info("email token: " + request.route_url( 'user_confirm_email', token=email_token(account))) if discussion: maybe_auto_subscribe(user, discussion) session.flush() return CreationResponse(user, Everyone, permissions) finally: session.autoflush = old_autoflush