예제 #1
0
파일: password.py 프로젝트: assembl/assembl
def verify_password_change_token(token, max_age=None):
    data, valid = verify_data_token(token, max_age=max_age)
    if valid == Validity.BAD_HASH:
        try:
            data = int(data)
        except:
            return None, Validity.INVALID_FORMAT
        user = User.get(data)
        if not user:
            return None, Validity.DATA_NOT_FOUND
        password = user.password.decode('iso-8859-1') if user.password else 'empty'
        data, valid = verify_data_token(token, password, max_age)
        return user, valid
    # Try decoding legacy
    try:
        id, hash = token.split('e', 1)
        id = int(id)
        user = User.get(id)
        if not user:
            return user, Validity.DATA_NOT_FOUND
        age = datetime.utcnow() - user.last_login
        if age > timedelta(days=3):
            return user, Validity.EXPIRED
        check = str(id) + user.last_login.isoformat()[:19]
        valid = verify_password(
            check, hash, HashEncoding.HEX)
        if not valid:
            return user, Validity.BAD_HASH
        return user, Validity.VALID
    except:
        return None, Validity.INVALID_FORMAT
예제 #2
0
파일: widget.py 프로젝트: Lornz-/assembl
def voting_widget_view(request):
    user_id = authenticated_userid(request) or Everyone
    ctx = request.context
    view = (request.matchdict or {}).get('view', None)\
        or ctx.get_default_view() or 'default'
    widget = ctx._instance
    permissions = get_permissions(
        user_id, ctx.get_discussion_id())
    json = widget.generic_json(view, user_id, permissions)
    #json['discussion'] = ...
    if user_id != Everyone:
        user = User.get(user_id)
        json['user'] = user.generic_json(view, user_id, permissions)
        json['user_permissions'] = get_permissions(
            user_id, widget.get_discussion_id())
        user_state = widget.get_user_state(user_id)
        if user_state is not None:
            json['user_state'] = user_state
    target_id = request.GET.get('target', None)
    if target_id and Idea.get_database_id(target_id):
        json['user_votes_url'] = widget.get_user_votes_url(target_id)
        json['voting_urls'] = widget.get_voting_urls(target_id)
    json['criteria'] = [idea.generic_json(view, user_id, permissions)
                        for idea in widget.criteria]
    return json
예제 #3
0
파일: views.py 프로젝트: hypnotics/assembl
def discussion_list_view(request):
    user_id = authenticated_userid(request) or Everyone
    user = None
    if user_id != Everyone:
        user = User.get(user_id)
    roles = get_roles(user_id)
    context = get_default_context(request)
    context['discussions'] = []
    #Show even anonymous users every discussion one has access to if 
    #authenticated, so they can login and read them
    discussions = discussions_with_access(Authenticated if user_id == Everyone else user_id)
    for discussion in discussions:
        discussionFrontendUrls = FrontendUrls(discussion)
        discussion_context = {
            'topic': discussion.topic,
            'slug': discussion.slug,
            'url': discussionFrontendUrls.get_discussion_url()
        }
        if user_has_permission(discussion.id, user_id, P_ADMIN_DISC):
            discussion_context['admin_url'] = discussionFrontendUrls.get_discussion_edition_url()
            discussion_context['permissions_url'] = request.route_url(
                'discussion_permissions', discussion_id=discussion.id)
        context['discussions'].append(discussion_context)
    if R_SYSADMIN in roles:
        context['discussions_admin_url'] = request.route_url('discussion_admin')
        context['permissions_admin_url'] = request.route_url('general_permissions')
    context['user'] = user
    return context
예제 #4
0
def maybe_merge(
        backend, details, user=None, other_users=None,
        *args, **kwargs):
    # If we do not already have a user, see if we're in a situation
    # where we're adding an account to an existing user, and maybe
    # even merging
    request = backend.strategy.request
    adding_account = request.session.get("add_account", None)
    if adding_account is not None:
        del request.session["add_account"]
    # current discussion and next?
    logged_in = request.authenticated_userid
    if logged_in:
        logged_in = User.get(logged_in)
        if adding_account:
            if user and user != logged_in:
                # logged_in presumably newer?
                logged_in.merge(user)
                logged_in.db.delete(user)
                logged_in.db.flush()
            user = logged_in
        else:
            forget(request)
    if other_users:
        if not user:
            user = other_users.pop(0)
        # Merge other accounts with same verified email
        for profile in other_users:
            user.merge(profile)
            profile.delete()
    return {"user": user}
예제 #5
0
파일: widget.py 프로젝트: Lornz-/assembl
def widget_view(request):
    # IF_OWNED not applicable for widgets... so far
    ctx = request.context
    user_id = authenticated_userid(request) or Everyone
    permissions = get_permissions(
        user_id, ctx.get_discussion_id())
    check_permissions(ctx, user_id, permissions, CrudPermissions.READ)
    view = (request.matchdict or {}).get('view', None)\
        or ctx.get_default_view() or 'default'
    json = ctx._instance.generic_json(view, user_id, permissions)
    # json['discussion'] = ...
    if user_id != Everyone:
        user = User.get(user_id)
        user_state = ctx._instance.get_user_state(user_id)
        json['user'] = user.generic_json(view, user_id, permissions)
        json['user_permissions'] = get_permissions(
            user_id, ctx._instance.get_discussion_id())
        if user_state is not None:
            json['user_state'] = user_state
    target_id = request.GET.get('target', None)
    if target_id:
        idea = Idea.get_instance(target_id)
        if idea:
            json['target'] = idea.generic_json(view, user_id, permissions)
        else:
            return HTTPNotFound("No idea "+target_id)
    return json
예제 #6
0
파일: views.py 프로젝트: rmoorman/assembl
def finish_password_change(request):
    logged_in = authenticated_userid(request)
    if not logged_in:
        raise HTTPUnauthorized()
    user = User.get(logged_in)
    localizer = request.localizer
    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
        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),
        slug_prefix=slug_prefix, error=error)
예제 #7
0
파일: views.py 프로젝트: rmoorman/assembl
def do_password_change(request):
    localizer = request.localizer
    token = request.matchdict.get('ticket')
    (verified, user_id) = verify_password_change_token(token, 24)

    if not verified:
        if not user_id:
            raise HTTPBadRequest(localizer.translate(_(
                "Wrong password token.")))
        else:
            return HTTPFound(location=maybe_contextual_route(
                request, 'password_change_sent', profile_id=user_id, _query=dict(
                    sent=True, error=localizer.translate(_(
                        "This token is expired. "
                        "Do you want us to send another?")))))
    user = User.get(user_id)
    headers = remember(request, user_id)
    request.response.headerlist.extend(headers)
    user.last_login = datetime.utcnow()
    slug = request.matchdict.get('discussion_slug', None)
    slug_prefix = "/" + slug if slug else ""
    return dict(
        get_default_context(request),
        slug_prefix=slug_prefix,
        title=localizer.translate(_('Change your password')))
예제 #8
0
파일: views.py 프로젝트: assembl/assembl
def request_password_change(request):
    localizer = request.localizer
    identifier = request.params.get('identifier') or ''
    user_id = request.params.get('user_id') or ''
    error = request.params.get('error') or ''
    user = None

    if user_id:
        try:
            user = User.get(int(user_id))
            identifier = identifier or user.get_preferred_email() or ''
        except:
            error = error or localizer.translate(_("This user cannot be found"))
    elif identifier:
        user, account = from_identifier(identifier)
        if user:
            user_id = user.id
        else:
            error = error or localizer.translate(_("This user cannot be found"))
    if error or not user:
        context = get_default_context(request)
        get_route = context['get_route']
        request.session.flash(error)
        return HTTPFound(location=get_route('react_request_password_change'))

    discussion_slug = request.matchdict.get('discussion_slug', None)
    route = 'password_change_sent'
    if discussion_slug:
        route = 'contextual_' + route
    return HTTPFound(location=maybe_contextual_route(
        request, 'password_change_sent', profile_id=user_id,
        _query=dict(email=identifier if '@' in identifier else '')))
예제 #9
0
def discussion_list_view(request):
    user_id = authenticated_userid(request) or Everyone
    user = None
    if user_id != Everyone:
        user = User.get(id=user_id)
    context = get_default_context()
    context['discussions'] = discussions_with_access(user_id)
    context['user'] = user
    return context
예제 #10
0
파일: auth.py 프로젝트: assembl/assembl
def add_local_role(request):
    # Do not use check_permissions, this is a special case
    ctx = request.context
    user_id = request.authenticated_userid
    if not user_id:
        raise HTTPUnauthorized()
    discussion_id = ctx.get_discussion_id()
    discussion = Discussion.get(discussion_id)
    user_uri = User.uri_generic(user_id)
    if discussion_id is None:
        raise HTTPBadRequest()
    permissions = get_permissions(user_id, discussion_id)
    json = request.json_body
    if "discussion" not in json:
        json["discussion"] = Discussion.uri_generic(discussion_id)
    requested_user = json.get('user', None)
    if not requested_user:
        json['user'] = requested_user = user_uri
    elif requested_user != user_uri and P_ADMIN_DISC not in permissions:
        raise HTTPUnauthorized()
    if P_ADMIN_DISC not in permissions:
        if P_SELF_REGISTER in permissions:
            json['requested'] = False
            json['role'] = R_PARTICIPANT
            req_user = User.get_instance(requested_user)
            if not discussion.check_authorized_email(req_user):
                raise HTTPForbidden()
        elif P_SELF_REGISTER_REQUEST in permissions:
            json['requested'] = True
        else:
            raise HTTPUnauthorized()
    try:
        instances = ctx.create_object("LocalUserRole", json, user_id)
    except HTTPClientError as e:
        raise e
    except Exception as e:
        raise HTTPBadRequest(e)
    if instances:
        first = instances[0]
        db = first.db
        for instance in instances:
            db.add(instance)
        db.flush()
        # Side effect: materialize subscriptions.
        if not first.requested:
            # relationship may not be initialized
            user = first.user or User.get(first.user_id)
            user.get_notification_subscriptions(discussion_id, True)

        # Update the user's AgentStatusInDiscussion
        user.update_agent_status_subscribe(discussion)

        view = request.GET.get('view', None) or 'default'
        permissions = get_permissions(
            user_id, ctx.get_discussion_id())
        return CreationResponse(first, user_id, permissions, view)
예제 #11
0
파일: auth.py 프로젝트: hypnotics/assembl
def add_local_role(request):
    # Do not use check_permissions, this is a special case
    ctx = request.context
    user_id = authenticated_userid(request)
    if user_id == Everyone:
        raise HTTPUnauthorized()
    discussion_id = ctx.get_discussion_id()
    user_uri = User.uri_generic(user_id)
    if discussion_id is None:
        raise HTTPBadRequest()
    permissions = get_permissions(user_id, discussion_id)
    json = request.json_body
    if "discussion" not in json:
        json["discussion"] = Discussion.uri_generic(discussion_id)
    requested_user = json.get('user', None)
    if not requested_user:
        json['user'] = requested_user = user_uri
    elif requested_user != user_uri and P_ADMIN_DISC not in permissions:
        raise HTTPUnauthorized()
    if P_ADMIN_DISC not in permissions:
        if P_SELF_REGISTER in permissions:
            json['requested'] = False
            json['role'] = R_PARTICIPANT
        elif P_SELF_REGISTER_REQUEST in permissions:
            json['requested'] = True
        else:
            raise HTTPUnauthorized()
    try:
        instances = ctx.create_object("LocalUserRole", json, user_id)
    except HTTPClientError as e:
        raise e
    except Exception as e:
        raise HTTPBadRequest(e)
    if instances:
        first = instances[0]
        db = first.db()
        for instance in instances:
            db.add(instance)
        db.flush()
        # Side effect: materialize subscriptions.
        if not first.requested:
            # relationship may not be initialized
            user = first.user or User.get(first.user_id)
            user.get_notification_subscriptions(discussion_id, True)
        view = request.GET.get('view', None) or 'default'
        permissions = get_permissions(
            user_id, ctx.get_discussion_id())
        return Response(
            dumps(first.generic_json(view, user_id, permissions)),
            location=first.uri_generic(first.id),
            status_code=201)
예제 #12
0
파일: auth.py 프로젝트: assembl/assembl
def delete_local_role(request):
    ctx = request.context
    instance = ctx._instance
    user_id = request.authenticated_userid
    if not user_id:
        raise HTTPUnauthorized()
    discussion_id = ctx.get_discussion_id()

    if discussion_id is None:
        raise HTTPBadRequest()
    permissions = get_permissions(user_id, discussion_id)
    requested_user = instance.user
    if requested_user.id != user_id and P_ADMIN_DISC not in permissions:
        raise HTTPUnauthorized()

    user = User.get(user_id)
    discussion = Discussion.get(discussion_id)
    instance.db.delete(instance)
    # Update the user's AgentStatusInDiscussion
    user.update_agent_status_unsubscribe(discussion)
    instance.db.flush()  # maybe unnecessary
    return {}
예제 #13
0
파일: auth.py 프로젝트: assembl/assembl
def set_local_role(request):
    # Do not use check_permissions, this is a special case
    ctx = request.context
    instance = ctx._instance
    user_id = request.authenticated_userid
    if not user_id:
        raise HTTPUnauthorized()
    discussion_id = ctx.get_discussion_id()
    user_uri = User.uri_generic(user_id)
    if discussion_id is None:
        raise HTTPBadRequest()
    permissions = get_permissions(user_id, discussion_id)
    json = request.json_body
    requested_user = json.get('user', None)
    if not requested_user:
        json['user'] = requested_user = user_uri
    elif requested_user != user_uri and P_ADMIN_DISC not in permissions:
        raise HTTPUnauthorized()
    if P_ADMIN_DISC not in permissions:
        if P_SELF_REGISTER in permissions:
            json['requested'] = False
            json['role'] = R_PARTICIPANT
        elif P_SELF_REGISTER_REQUEST in permissions:
            json['requested'] = True
        else:
            raise HTTPUnauthorized()
    updated = instance.update_from_json(json, user_id, ctx)
    view = request.GET.get('view', None) or 'default'

    # Update the user's AgentStatusInDiscussion
    user = User.get(user_id)
    discussion = Discussion.get(discussion_id)
    user.update_agent_status_subscribe(discussion)

    if view == 'id_only':
        return [updated.uri()]
    else:
        return updated.generic_json(view, user_id, permissions)
예제 #14
0
def set_local_role(request):
    # Do not use check_permissions, this is a special case
    ctx = request.context
    instance = ctx._instance
    user_id = authenticated_userid(request)
    if not user_id:
        raise HTTPUnauthorized()
    discussion_id = ctx.get_discussion_id()
    user_uri = User.uri_generic(user_id)
    if discussion_id is None:
        raise HTTPBadRequest()
    permissions = ctx.get_permissions()
    json = request.json_body
    requested_user = json.get('user', None)
    if not requested_user:
        json['user'] = requested_user = user_uri
    elif requested_user != user_uri and P_ADMIN_DISC not in permissions:
        raise HTTPUnauthorized()
    if P_ADMIN_DISC not in permissions:
        if P_SELF_REGISTER in permissions:
            json['requested'] = False
            json['role'] = R_PARTICIPANT
        elif P_SELF_REGISTER_REQUEST in permissions:
            json['requested'] = True
        else:
            raise HTTPUnauthorized()
    updated = instance.update_from_json(json, user_id, ctx)
    view = request.GET.get('view', None) or 'default'

    # Update the user's AgentStatusInDiscussion
    user = User.get(user_id)
    discussion = Discussion.get(discussion_id)
    user.update_agent_status_subscribe(discussion)

    if view == 'id_only':
        return [updated.uri()]
    else:
        return updated.generic_json(view, user_id, permissions)
예제 #15
0
def maybe_social_logout(request):
    """If the user has a an account with the default social provider,
    and that account has a logout URL, redirect there.
    Maybe the next argument should be carried?"""
    discussion = discussion_from_request(request)
    if not discussion:
        return
    backend_name = discussion.preferences['authorization_server_backend']
    if not backend_name:
        return
    user_id = request.authenticated_userid
    if not user_id:
        return
    user = User.get(user_id)
    for account in user.accounts:
        if getattr(account, 'provider_with_idp', None) == backend_name:
            break
    else:
        return
    # TODO: tell the account that the login has expired.
    # Also check if already expired?
    return config.get('SOCIAL_AUTH_%s_LOGOUT_URL' %
                      (account.provider.upper(), ))
예제 #16
0
def maybe_social_logout(request):
    """If the user has a an account with the default social provider,
    and that account has a logout URL, redirect there.
    Maybe the next argument should be carried?"""
    discussion = discussion_from_request(request)
    if not discussion:
        return
    backend_name = discussion.preferences['authorization_server_backend']
    if not backend_name:
        return
    user_id = request.authenticated_userid
    if not user_id:
        return
    user = User.get(user_id)
    for account in user.accounts:
        if getattr(account, 'provider_with_idp', None) == backend_name:
            break
    else:
        return
    # TODO: tell the account that the login has expired.
    # Also check if already expired?
    return config.get('SOCIAL_AUTH_%s_LOGOUT_URL' % (
        account.provider.upper(),))
예제 #17
0
파일: views.py 프로젝트: rmoorman/assembl
def finish_password_change(request):
    logged_in = authenticated_userid(request)
    if not logged_in:
        raise HTTPUnauthorized()
    user = User.get(logged_in)
    localizer = request.localizer
    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
        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),
                slug_prefix=slug_prefix,
                error=error)
예제 #18
0
def maybe_merge(backend,
                details,
                user=None,
                other_users=None,
                *args,
                **kwargs):
    # If we do not already have a user, see if we're in a situation
    # where we're adding an account to an existing user, and maybe
    # even merging
    request = backend.strategy.request
    adding_account = request.session.get("add_account", None)
    if adding_account is not None:
        del request.session["add_account"]
    # current discussion and next?
    logged_in = authenticated_userid(request)
    if logged_in:
        logged_in = User.get(logged_in)
        if adding_account:
            if user and user != logged_in:
                # logged_in presumably newer?
                logged_in.merge(user)
                logged_in.db.delete(user)
                logged_in.db.flush()
            user = logged_in
        else:
            forget(request)
            user = None
            logged_in = None
    if other_users:
        if not user:
            user = other_users.pop(0)
        # Merge other accounts with same verified email
        for profile in other_users:
            user.merge(profile)
            profile.delete()
    return {"user": user}
예제 #19
0
def request_password_change(request):
    localizer = request.localizer
    identifier = request.params.get('identifier') or ''
    user_id = request.params.get('user_id') or ''
    error = request.params.get('error') or ''
    user = None
    if user_id:
        try:
            user = User.get(int(user_id))
            identifier = identifier or user.get_preferred_email() or ''
        except:
            error = error or localizer.translate(_("This user cannot be found"))
    elif identifier:
        user, account = from_identifier(identifier)
        if user:
            user_id = user.id
        else:
            error = error or localizer.translate(_("This user cannot be found"))
    if error or not user:
        slug = request.matchdict.get('discussion_slug', None)

        return dict(
            get_default_context(request),
            error=error,
            user_id=user_id,
            identifier=identifier,
            slug_prefix="/" + slug if slug else "",
            title=localizer.translate(_('I forgot my password')))

    discussion_slug = request.matchdict.get('discussion_slug', None)
    route = 'password_change_sent'
    if discussion_slug:
        route = 'contextual_' + route
    return HTTPFound(location=maybe_contextual_route(
        request, 'password_change_sent', profile_id=user_id,
        _query=dict(email=identifier if '@' in identifier else '')))
예제 #20
0
def discussion_list_view(request):
    request.session.pop('discussion')
    user_id = request.authenticated_userid or Everyone
    user = None
    if user_id != Everyone:
        user = User.get(user_id)
    roles = get_roles(user_id)
    context = get_default_context(request)
    context['discussions'] = []
    # Show even anonymous users every discussion one has access to if
    # authenticated, so they can login and read them
    discussions = discussions_with_access(Authenticated if user_id ==
                                          Everyone else user_id)
    for discussion in discussions:
        discussionFrontendUrls = FrontendUrls(discussion)
        discussion_context = {
            'topic': discussion.topic,
            'slug': discussion.slug,
            'url': discussionFrontendUrls.get_discussion_url()
        }
        if user_has_permission(discussion.id, user_id, P_ADMIN_DISC):
            discussion_context[
                'admin_url'] = discussionFrontendUrls.get_discussion_edition_url(
                )
            discussion_context['permissions_url'] = request.route_url(
                'discussion_permissions', discussion_id=discussion.id)
        context['discussions'].append(discussion_context)
    if R_SYSADMIN in roles:
        context['discussions_admin_url'] = request.route_url(
            'discussion_admin')
        context['permissions_admin_url'] = request.route_url(
            'general_permissions')
        context['preferences_admin_url'] = request.route_url(
            'admin_global_preferences')
    context['user'] = user
    return context
예제 #21
0
def request_password_change(request):
    localizer = request.localizer
    identifier = request.params.get('identifier') or ''
    user_id = request.params.get('user_id') or ''
    error = request.params.get('error') or ''
    user = None

    if user_id:
        try:
            user = User.get(int(user_id))
            identifier = identifier or user.get_preferred_email() or ''
        except:
            error = error or localizer.translate(
                _("This user cannot be found"))
    elif identifier:
        user, account = from_identifier(identifier)
        if user:
            user_id = user.id
        else:
            error = error or localizer.translate(
                _("This user cannot be found"))
    if error or not user:
        context = get_default_context(request)
        get_route = context['get_route']
        request.session.flash(error)
        return HTTPFound(location=get_route('react_request_password_change'))

    discussion_slug = request.matchdict.get('discussion_slug', None)
    route = 'password_change_sent'
    if discussion_slug:
        route = 'contextual_' + route
    return HTTPFound(location=maybe_contextual_route(
        request,
        'password_change_sent',
        profile_id=user_id,
        _query=dict(email=identifier if '@' in identifier else '')))
예제 #22
0
파일: views.py 프로젝트: festrade/assembl
def request_password_change(request):
    localizer = request.localizer
    identifier = request.params.get('identifier') or ''
    user_id = request.params.get('user_id') or ''
    error = request.params.get('error') or ''
    user = None
    if user_id:
        try:
            user = User.get(int(user_id))
            identifier = identifier or user.get_preferred_email() or ''
        except:
            error = error or localizer.translate(_("This user cannot be found"))
    elif identifier:
        user, account = from_identifier(identifier)
        if user:
            user_id = user.id
        else:
            error = error or localizer.translate(_("This user cannot be found"))
    if error or not user:
        slug = request.matchdict.get('discussion_slug', None)

        return dict(
            get_default_context(request),
            error=error,
            user_id=user_id,
            identifier=identifier,
            slug_prefix="/" + slug if slug else "",
            title=localizer.translate(_('I forgot my password')))

    discussion_slug = request.matchdict.get('discussion_slug', None)
    route = 'password_change_sent'
    if discussion_slug:
        route = 'contextual_' + route
    return HTTPFound(location=maybe_contextual_route(
        request, 'password_change_sent', profile_id=user_id,
        _query=dict(email=identifier if '@' in identifier else '')))
예제 #23
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)
예제 #24
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)