예제 #1
0
파일: views.py 프로젝트: festrade/assembl
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))
예제 #2
0
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.')))
예제 #3
0
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))
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
파일: views.py 프로젝트: assembl/assembl
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)
예제 #7
0
파일: __init__.py 프로젝트: jean/assembl
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
예제 #8
0
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)
예제 #9
0
파일: views.py 프로젝트: festrade/assembl
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.')))
예제 #10
0
파일: views.py 프로젝트: assembl/assembl
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)
예제 #11
0
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)
예제 #12
0
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)
예제 #13
0
파일: views.py 프로젝트: festrade/assembl
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)
예제 #14
0
파일: views.py 프로젝트: assembl/assembl
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)
예제 #15
0
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)
예제 #16
0
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)
예제 #17
0
파일: views.py 프로젝트: assembl/assembl
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)
예제 #18
0
파일: locale.py 프로젝트: n0izn0iz/assembl
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
예제 #19
0
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)
예제 #20
0
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
예제 #21
0
파일: views.py 프로젝트: assembl/assembl
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)
예제 #22
0
파일: views.py 프로젝트: driver4567/assembl
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)
예제 #23
0
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
예제 #24
0
파일: views.py 프로젝트: rmoorman/assembl
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)
예제 #25
0
파일: __init__.py 프로젝트: assembl/assembl
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
예제 #26
0
파일: views.py 프로젝트: rmoorman/assembl
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))
예제 #27
0
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)))
예제 #28
0
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)))
예제 #29
0
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))
예제 #30
0
파일: views.py 프로젝트: assembl/assembl
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))
예제 #31
0
파일: auth.py 프로젝트: assembl/assembl
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
예제 #32
0
파일: views.py 프로젝트: iilab/assembl
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)
예제 #33
0
파일: views.py 프로젝트: festrade/assembl
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'))
예제 #34
0
파일: views.py 프로젝트: rmoorman/assembl
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)
예제 #35
0
파일: views.py 프로젝트: rmoorman/assembl
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))
예제 #36
0
파일: auth.py 프로젝트: shangxor/assembl
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
예제 #37
0
파일: views.py 프로젝트: festrade/assembl
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)
예제 #38
0
파일: views.py 프로젝트: rmoorman/assembl
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)
예제 #39
0
파일: auth.py 프로젝트: cimadure/idealoom
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