Ejemplo n.º 1
0
def login(request, redirect_field_name=REDIRECT_FIELD_NAME, _form_class=LoginForm):
    # TODO: Logging in should reset request.user
    # TODO: Configure the login view as the default view for not having
    #       permission to view something.
    if request.authenticated_userid is not None:
        return HTTPSeeOther(request.route_path("manage.projects"))

    user_service = request.find_service(IUserService, context=None)
    breach_service = request.find_service(IPasswordBreachedService, context=None)

    redirect_to = request.POST.get(
        redirect_field_name, request.GET.get(redirect_field_name)
    )

    form = _form_class(
        request.POST,
        request=request,
        user_service=user_service,
        breach_service=breach_service,
        check_password_metrics_tags=["method:auth", "auth_method:login_form"],
    )

    if request.method == "POST":
        if form.validate():
            # Get the user id for the given username.
            username = form.username.data
            userid = user_service.find_userid(username)

            # If the user-originating redirection url is not safe, then
            # redirect to the index instead.
            if not redirect_to or not is_safe_url(url=redirect_to, host=request.host):
                redirect_to = request.route_path("manage.projects")

            # Actually perform the login routine for our user.
            headers = _login_user(request, userid)

            # Now that we're logged in we'll want to redirect the user to
            # either where they were trying to go originally, or to the default
            # view.
            resp = HTTPSeeOther(redirect_to, headers=dict(headers))

            # We'll use this cookie so that client side javascript can
            # Determine the actual user ID (not username, user ID). This is
            # *not* a security sensitive context and it *MUST* not be used
            # where security matters.
            #
            # We'll also hash this value just to avoid leaking the actual User
            # IDs here, even though it really shouldn't matter.
            resp.set_cookie(
                USER_ID_INSECURE_COOKIE,
                hashlib.blake2b(str(userid).encode("ascii"), person=b"warehouse.userid")
                .hexdigest()
                .lower(),
            )
            return resp

    return {
        "form": form,
        "redirect": {"field": REDIRECT_FIELD_NAME, "data": redirect_to},
    }
Ejemplo n.º 2
0
    def login(self):
        self.csrf_valid()

        try:
            schema = s.UserSchema.create_schema(self.request)
            deserialized = schema.deserialize(self.cstruct)

            headers = remember(self.request, deserialized['email'])
            token = self.request.session.new_csrf_token()

            response = HTTPSeeOther(location=self.request.route_url('main', traverse='user'), headers=headers)
            response.set_cookie('CSRF-Token', token, max_age=864000, overwrite=True)

            return response
        except colander.Invalid as e:
            self.request.response.status = 422

            form_error = None
            field_errors = e.asdict()

            if 'email' in field_errors and 'password' in field_errors:
                if field_errors['email'] == field_errors['password']:
                    form_error = "Username or password is incorrect."

            return {
                    'errors': field_errors,
                    'form_error': form_error,
                    }
Ejemplo n.º 3
0
def change_language(request):
    lang = request.matchdict.get('lang')
    resp = HTTPSeeOther(request.application_url)

    if lang is None:
        resp.unset_cookie('_LOCALE_')
    else:
        resp.set_cookie('_LOCALE_', lang, max_age=timedelta(days=365))  # max_age = year

    return resp
Ejemplo n.º 4
0
def login(request, redirect_field_name=REDIRECT_FIELD_NAME,
          _form_class=forms.LoginForm):
    # TODO: Logging in should reset request.user
    # TODO: Configure the login view as the default view for not having
    #       permission to view something.

    user_service = request.find_service(IUserService, context=None)

    redirect_to = request.POST.get(redirect_field_name,
                                   request.GET.get(redirect_field_name))

    form = _form_class(request.POST, user_service=user_service)

    if request.method == "POST" and form.validate():
        # Get the user id for the given username.
        username = form.username.data
        userid = user_service.find_userid(username)

        # If the user-originating redirection url is not safe, then redirect to
        # the index instead.
        if (not redirect_to or
                not is_safe_url(url=redirect_to, host=request.host)):
            redirect_to = "/"

        # Actually perform the login routine for our user.
        headers = _login_user(request, userid)

        # Now that we're logged in we'll want to redirect the user to either
        # where they were trying to go originally, or to the default view.
        resp = HTTPSeeOther(redirect_to, headers=dict(headers))

        # We'll use this cookie so that client side javascript can Determine
        # the actual user ID (not username, user ID). This is *not* a security
        # sensitive context and it *MUST* not be used where security matters.
        #
        # We'll also hash this value just to avoid leaking the actual User IDs
        # here, even though it really shouldn't matter.
        resp.set_cookie(
            USER_ID_INSECURE_COOKIE,
            blake2b(
                str(userid).encode("ascii"),
                person=b"warehouse.userid",
            ).hexdigest().lower(),
        )

        return resp

    return {
        "form": form,
        "redirect": {
            "field": REDIRECT_FIELD_NAME,
            "data": redirect_to,
        },
    }
Ejemplo n.º 5
0
def set_color_theme(request):
    cookie_name = 'color_theme'
    current = request.cookies.get(cookie_name, 'default')
    new_theme = request.POST.get('color_theme')
    response = HTTPSeeOther(request.route_url('preferences'))
    if new_theme and new_theme != current:
        cookie_path = '/'  # FIXME: not necessarily
        if new_theme == 'default':
            response.delete_cookie(cookie_name, path=cookie_path)
        else:
            response.set_cookie(cookie_name, new_theme, path=cookie_path)
    return response
Ejemplo n.º 6
0
def two_factor(request, _form_class=TwoFactorForm):
    if request.authenticated_userid is not None:
        return HTTPSeeOther(request.route_path("manage.projects"))

    token_service = request.find_service(ITokenService, name="two_factor")

    try:
        two_factor_data = token_service.loads(request.query_string)
    except TokenException:
        request.session.flash("Invalid or expired two factor login.", queue="error")
        return HTTPSeeOther(request.route_path("accounts.login"))

    userid = two_factor_data.get("userid")
    if not userid:
        return HTTPSeeOther(request.route_path("accounts.login"))

    redirect_to = two_factor_data.get("redirect_to")

    user_service = request.find_service(IUserService, context=None)

    form = _form_class(
        request.POST,
        user_id=userid,
        user_service=user_service,
        check_password_metrics_tags=["method:auth", "auth_method:login_form"],
    )

    if request.method == "POST":
        if form.validate():
            # If the user-originating redirection url is not safe, then
            # redirect to the index instead.
            if not redirect_to or not is_safe_url(url=redirect_to, host=request.host):
                redirect_to = request.route_path("manage.projects")

            _login_user(request, userid)

            resp = HTTPSeeOther(redirect_to)
            resp.set_cookie(
                USER_ID_INSECURE_COOKIE,
                hashlib.blake2b(str(userid).encode("ascii"), person=b"warehouse.userid")
                .hexdigest()
                .lower(),
            )

            return resp

    return {"form": form}
Ejemplo n.º 7
0
def logout(request, redirect_field_name=REDIRECT_FIELD_NAME):
    # TODO: Logging out should reset request.user

    redirect_to = request.POST.get(
        redirect_field_name, request.GET.get(redirect_field_name)
    )

    # If the user-originating redirection url is not safe, then redirect to
    # the index instead.
    if not redirect_to or not is_safe_url(url=redirect_to, host=request.host):
        redirect_to = "/"

    # If we're already logged out, then we'll go ahead and issue our redirect right
    # away instead of trying to log a non-existent user out.
    if request.user is None:
        return HTTPSeeOther(redirect_to)

    if request.method == "POST":
        # A POST to the logout view tells us to logout. There's no form to
        # validate here because there's no data. We should be protected against
        # CSRF attacks still because of the CSRF framework, so users will still
        # need a post body that contains the CSRF token.
        headers = forget(request)

        # When crossing an authentication boundary we want to create a new
        # session identifier. We don't want to keep any information in the
        # session when going from authenticated to unauthenticated because
        # user's generally expect that logging out is a destructive action
        # that erases all of their private data. However, if we don't clear the
        # session then another user can use the computer after them, log in to
        # their account, and then gain access to anything sensitive stored in
        # the session for the original user.
        request.session.invalidate()

        # Now that we're logged out we'll want to redirect the user to either
        # where they were originally, or to the default view.
        resp = HTTPSeeOther(redirect_to, headers=dict(headers))

        # Ensure that we delete our user_id__insecure cookie, since the user is
        # no longer logged in.
        resp.delete_cookie(USER_ID_INSECURE_COOKIE)

        return resp

    return {"redirect": {"field": REDIRECT_FIELD_NAME, "data": redirect_to}}
Ejemplo n.º 8
0
    def delete_project_release_file(self):
        def _error(message):
            self.request.session.flash(message, queue="error")
            return HTTPSeeOther(
                self.request.route_path(
                    "manage.project.release",
                    project_name=self.release.project.name,
                    version=self.release.version,
                )
            )

        if self.request.flags.enabled(AdminFlagValue.DISALLOW_DELETION):
            message = (
                "Project deletion temporarily disabled. "
                "See https://pypi.org/help#admin-intervention for details."
            )
            return _error(message)

        project_name = self.request.POST.get("confirm_project_name")

        if not project_name:
            return _error("Confirm the request")

        try:
            release_file = (
                self.request.db.query(File)
                .filter(
                    File.release == self.release,
                    File.id == self.request.POST.get("file_id"),
                )
                .one()
            )
        except NoResultFound:
            return _error("Could not find file")

        if project_name != self.release.project.name:
            return _error(
                "Could not delete file - " + f"{project_name!r} is not the same as "
                f"{self.release.project.name!r}"
            )

        self.request.db.add(
            JournalEntry(
                name=self.release.project.name,
                action=f"remove file {release_file.filename}",
                version=self.release.version,
                submitted_by=self.request.user,
                submitted_from=self.request.remote_addr,
            )
        )

        self.release.project.record_event(
            tag="project:release:file:remove",
            ip_address=self.request.remote_addr,
            additional={
                "submitted_by": self.request.user.username,
                "canonical_version": self.release.canonical_version,
                "filename": release_file.filename,
            },
        )

        self.request.db.delete(release_file)

        self.request.session.flash(
            f"Deleted file {release_file.filename!r}", queue="success"
        )

        return HTTPSeeOther(
            self.request.route_path(
                "manage.project.release",
                project_name=self.release.project.name,
                version=self.release.version,
            )
        )
Ejemplo n.º 9
0
def verify_project_role(request):
    token_service = request.find_service(ITokenService, name="email")
    user_service = request.find_service(IUserService, context=None)

    def _error(message):
        request.session.flash(message, queue="error")
        return HTTPSeeOther(request.route_path("manage.projects"))

    try:
        token = request.params.get("token")
        data = token_service.loads(token)
    except TokenExpired:
        return _error(
            request._("Expired token: request a new project role invite"))
    except TokenInvalid:
        return _error(
            request._("Invalid token: request a new project role invite"))
    except TokenMissing:
        return _error(request._("Invalid token: no token supplied"))

    # Check whether this token is being used correctly
    if data.get("action") != "email-project-role-verify":
        return _error(
            request._("Invalid token: not a collaboration invitation token"))

    user = user_service.get_user(data.get("user_id"))
    if user != request.user:
        return _error(request._("Role invitation is not valid."))

    project = (request.db.query(Project).filter(
        Project.id == data.get("project_id")).one())
    desired_role = data.get("desired_role")

    role_invite = (request.db.query(RoleInvitation).filter(
        RoleInvitation.project == project).filter(
            RoleInvitation.user == user).one_or_none())

    if not role_invite:
        return _error(request._("Role invitation no longer exists."))

    # Use the renderer to bring up a confirmation page
    # before adding as contributor
    if request.method == "GET":
        return {
            "project_name": project.name,
            "desired_role": desired_role,
        }
    elif request.method == "POST" and "decline" in request.POST:
        request.db.delete(role_invite)
        request.session.flash(
            request._(
                "Invitation for '${project_name}' is declined.",
                mapping={"project_name": project.name},
            ),
            queue="success",
        )
        return HTTPSeeOther(request.route_path("manage.projects"))

    request.db.add(Role(user=user, project=project, role_name=desired_role))
    request.db.delete(role_invite)
    request.db.add(
        JournalEntry(
            name=project.name,
            action=f"accepted {desired_role} {user.username}",
            submitted_by=request.user,
            submitted_from=request.remote_addr,
        ))
    project.record_event(
        tag="project:role:accepted",
        ip_address=request.remote_addr,
        additional={
            "submitted_by": request.user.username,
            "role_name": desired_role,
            "target_user": user.username,
        },
    )
    user.record_event(
        tag="account:role:accepted",
        ip_address=request.remote_addr,
        additional={
            "submitted_by": request.user.username,
            "project_name": project.name,
            "role_name": desired_role,
        },
    )

    owner_roles = (request.db.query(Role).filter(
        Role.project == project).filter(Role.role_name == "Owner").all())
    owner_users = {owner.user for owner in owner_roles}

    # Don't send email to new user if they are now an owner
    owner_users.discard(user)

    submitter_user = user_service.get_user(data.get("submitter_id"))
    send_collaborator_added_email(
        request,
        owner_users,
        user=user,
        submitter=submitter_user,
        project_name=project.name,
        role=desired_role,
    )

    send_added_as_collaborator_email(
        request,
        user,
        submitter=submitter_user,
        project_name=project.name,
        role=desired_role,
    )

    request.session.flash(
        request._(
            "You are now ${role} of the '${project_name}' project.",
            mapping={
                "project_name": project.name,
                "role": desired_role
            },
        ),
        queue="success",
    )

    if desired_role == "Owner":
        return HTTPSeeOther(
            request.route_path("manage.project.roles",
                               project_name=project.name))
    else:
        return HTTPSeeOther(
            request.route_path("packaging.project", name=project.name))
Ejemplo n.º 10
0
def delete_project(project, request):
    confirm_project(project, request, fail_route="manage.project.settings")
    remove_project(project, request)

    return HTTPSeeOther(request.route_path('manage.projects'))
Ejemplo n.º 11
0
def change_project_role(project, request, _form_class=ChangeRoleForm):
    # TODO: This view was modified to handle deleting multiple roles for a
    # single user and should be updated when fixing GH-2745

    form = _form_class(request.POST)

    if form.validate():
        role_ids = request.POST.getall("role_id")

        if len(role_ids) > 1:
            # This user has more than one role, so just delete all the ones
            # that aren't what we want.
            #
            # TODO: This branch should be removed when fixing GH-2745.
            roles = (request.db.query(Role).join(User).filter(
                Role.id.in_(role_ids),
                Role.project == project,
                Role.role_name != form.role_name.data,
            ).all())
            removing_self = any(
                role.role_name == "Owner" and role.user == request.user
                for role in roles)
            if removing_self:
                request.session.flash("Cannot remove yourself as Owner",
                                      queue="error")
            else:
                for role in roles:
                    request.db.delete(role)
                    request.db.add(
                        JournalEntry(
                            name=project.name,
                            action=
                            f"remove {role.role_name} {role.user.username}",
                            submitted_by=request.user,
                            submitted_from=request.remote_addr,
                        ))
                    project.record_event(
                        tag="project:role:delete",
                        ip_address=request.remote_addr,
                        additional={
                            "submitted_by": request.user.username,
                            "role_name": role.role_name,
                            "target_user": role.user.username,
                        },
                    )
                request.session.flash("Changed role", queue="success")
        else:
            # This user only has one role, so get it and change the type.
            try:
                role = (request.db.query(Role).join(User).filter(
                    Role.id == request.POST.get("role_id"),
                    Role.project == project).one())
                if role.role_name == "Owner" and role.user == request.user:
                    request.session.flash("Cannot remove yourself as Owner",
                                          queue="error")
                else:
                    request.db.add(
                        JournalEntry(
                            name=project.name,
                            action="change {} {} to {}".format(
                                role.role_name, role.user.username,
                                form.role_name.data),
                            submitted_by=request.user,
                            submitted_from=request.remote_addr,
                        ))
                    role.role_name = form.role_name.data
                    project.record_event(
                        tag="project:role:change",
                        ip_address=request.remote_addr,
                        additional={
                            "submitted_by": request.user.username,
                            "role_name": form.role_name.data,
                            "target_user": role.user.username,
                        },
                    )
                    request.session.flash("Changed role", queue="success")
            except NoResultFound:
                request.session.flash("Could not find role", queue="error")

    return HTTPSeeOther(
        request.route_path("manage.project.roles", project_name=project.name))
Ejemplo n.º 12
0
 def _error(message):
     request.session.flash(message, queue="error")
     return HTTPSeeOther(
         request.route_path("accounts.request-password-reset"))
Ejemplo n.º 13
0
def join(group, request):
    groups_service = request.find_service(name='groups')
    groups_service.member_join(group, request.authenticated_userid)

    url = request.route_path('group_read', pubid=group.pubid, slug=group.slug)
    return HTTPSeeOther(url)
Ejemplo n.º 14
0
def verify_email(request):
    token_service = request.find_service(ITokenService, name="email")
    email_limiter = request.find_service(IRateLimiter, name="email.add")

    def _error(message):
        request.session.flash(message, queue="error")
        return HTTPSeeOther(request.route_path("manage.account"))

    try:
        token = request.params.get("token")
        data = token_service.loads(token)
    except TokenExpired:
        return _error(
            request._("Expired token: request a new email verification link"))
    except TokenInvalid:
        return _error(
            request._("Invalid token: request a new email verification link"))
    except TokenMissing:
        return _error(request._("Invalid token: no token supplied"))

    # Check whether this token is being used correctly
    if data.get("action") != "email-verify":
        return _error(
            request._("Invalid token: not an email verification token"))

    try:
        email = (request.db.query(Email).filter(
            Email.id == data["email.id"], Email.user == request.user).one())
    except NoResultFound:
        return _error(request._("Email not found"))

    if email.verified:
        return _error(request._("Email already verified"))

    email.verified = True
    email.unverify_reason = None
    email.transient_bounces = 0
    email.user.record_event(
        tag="account:email:verified",
        ip_address=request.remote_addr,
        additional={
            "email": email.email,
            "primary": email.primary
        },
    )

    # Reset the email-adding rate limiter for this IP address
    email_limiter.clear(request.remote_addr)

    if not email.primary:
        confirm_message = request._(
            "You can now set this email as your primary address")
    else:
        confirm_message = request._("This is your primary address")

    request.user.is_active = True

    request.session.flash(
        request._(
            "Email address ${email_address} verified. ${confirm_message}.",
            mapping={
                "email_address": email.email,
                "confirm_message": confirm_message
            },
        ),
        queue="success",
    )
    return HTTPSeeOther(request.route_path("manage.account"))
Ejemplo n.º 15
0
def login(request,
          redirect_field_name=REDIRECT_FIELD_NAME,
          _form_class=LoginForm):
    # TODO: Logging in should reset request.user
    # TODO: Configure the login view as the default view for not having
    #       permission to view something.
    if request.authenticated_userid is not None:
        return HTTPSeeOther(request.route_path("manage.projects"))

    user_service = request.find_service(IUserService, context=None)
    breach_service = request.find_service(IPasswordBreachedService,
                                          context=None)

    redirect_to = request.POST.get(redirect_field_name,
                                   request.GET.get(redirect_field_name))

    form = _form_class(
        request.POST,
        request=request,
        user_service=user_service,
        breach_service=breach_service,
        check_password_metrics_tags=["method:auth", "auth_method:login_form"],
    )

    if request.method == "POST":
        if form.validate():
            # Get the user id for the given username.
            username = form.username.data
            userid = user_service.find_userid(username)

            # If the user has enabled two factor authentication.
            if user_service.has_two_factor(userid):
                two_factor_data = {"userid": userid}
                if redirect_to:
                    two_factor_data["redirect_to"] = redirect_to

                token_service = request.find_service(ITokenService,
                                                     name="two_factor")
                token = token_service.dumps(two_factor_data)

                # Stuff our token in the query and redirect to two-factor page.
                resp = HTTPSeeOther(
                    request.route_path("accounts.two-factor", _query=token))
                return resp
            else:
                # If the user-originating redirection url is not safe, then
                # redirect to the index instead.
                if not redirect_to or not is_safe_url(url=redirect_to,
                                                      host=request.host):
                    redirect_to = request.route_path("manage.projects")

                # Actually perform the login routine for our user.
                headers = _login_user(request, userid)

                # Now that we're logged in we'll want to redirect the user to
                # either where they were trying to go originally, or to the default
                # view.
                resp = HTTPSeeOther(redirect_to, headers=dict(headers))

                # We'll use this cookie so that client side javascript can
                # Determine the actual user ID (not username, user ID). This is
                # *not* a security sensitive context and it *MUST* not be used
                # where security matters.
                #
                # We'll also hash this value just to avoid leaking the actual User
                # IDs here, even though it really shouldn't matter.
                resp.set_cookie(
                    USER_ID_INSECURE_COOKIE,
                    hashlib.blake2b(
                        str(userid).encode("ascii"),
                        person=b"warehouse.userid").hexdigest().lower(),
                )
            return resp

    return {
        "form": form,
        "redirect": {
            "field": REDIRECT_FIELD_NAME,
            "data": redirect_to
        },
    }
Ejemplo n.º 16
0
 def bail():
     # Abort registration; typically if the request is nonsensical
     clear_pending()
     request.session.flash('Your session expired.  Please try logging in again.')
     return HTTPSeeOther(location=request.route_url('account.login'))
Ejemplo n.º 17
0
def register(context, request):
    def clear_pending():
        request.session.pop('pending_identity_email', None)
        request.session.pop('pending_identity_url', None)
        request.session.pop('pending_identity_webfinger', None)

    def bail():
        # Abort registration; typically if the request is nonsensical
        clear_pending()
        request.session.flash('Your session expired.  Please try logging in again.')
        return HTTPSeeOther(location=request.route_url('account.login'))

    # Check identity URL

    identity_url = request.session.get('pending_identity_url')
    identity_email = request.session.get('pending_identity_email')
    openid_q = model.session.query(IdentityURL).filter_by(url=identity_url)
    persona_q = model.session.query(IdentityEmail).filter_by(email=identity_email)

    # Must register against (or add) exactly one ID
    if not identity_url and not identity_email:
        return bail()
    if identity_url and identity_email:
        return bail()

    # Cannot re-register an ID
    if identity_url and openid_q.count():
        return bail()
    if identity_email and persona_q.count():
        return bail()

    form = RegistrationForm(request.POST)

    if request.method != 'POST' or not form.validate():
        return render_to_response('account/register.mako', {
                'form': form,
                'identity_email': identity_email,
                'identity_url': identity_url,
                'identity_webfinger': request.session.get('pending_identity_webfinger'),
            },
            request=request)

    # XXX waiting on auth_dev2 branch to merge to factor this out of controls
    from floof.lib.helpers import reduce_display_name
    if not form.display_name.data:
        display_name = None
        has_trivial_display_name = False
    else:
        display_name = form.display_name.data
        has_trivial_display_name = (form.username.data ==
            reduce_display_name(display_name))

    # Create db records
    resource = Resource(type=u'users')
    discussion = Discussion(resource=resource)
    user = User(
        name=form.username.data,
        email=form.email.data,
        resource=resource,
        timezone=form.timezone.data,

        display_name=display_name,
        has_trivial_display_name=has_trivial_display_name,
    )
    model.session.add_all((user, resource, discussion))

    base_user = model.session.query(Role).filter_by(name=u'user').one()
    user.roles.append(base_user)

    if identity_url:
        openid = IdentityURL(url=identity_url)
        user.identity_urls.append(openid)
    else:
        persona_id = IdentityEmail(email=identity_email)
        user.identity_emails.append(persona_id)

    model.session.flush()

    log.info('User #{0} registered: {1}'.format(user.id, user.name))

    # Log 'em in
    clear_pending()
    auth_headers = security.forget(request)
    headers = security.remember(
            request, user, openid_url=identity_url,
            persona_addr=identity_email)
    if headers is None:
        log.error("Failed to log in new registrant.")  # shouldn't happen
    else:
        auth_headers += headers

    # And off we go
    return HTTPSeeOther(request.route_url('root'), headers=auth_headers)
Ejemplo n.º 18
0
 def retry():
     """Redirect to the login page, preserving the return key (if any)."""
     location = request.route_url('account.login')
     if return_key:
         location = update_params(location, return_key=return_key)
     return HTTPSeeOther(location=location)
Ejemplo n.º 19
0
def login_finish(context, request):
    """Step two of logging in; the OpenID provider redirects back here."""

    def retry():
        """Redirect to the login page, preserving the return key (if any)."""
        location = request.route_url('account.login')
        if return_key:
            location = update_params(location, return_key=return_key)
        return HTTPSeeOther(location=location)

    return_url = request.route_url('account.login_finish')

    return_key = key_from_request(request)
    if return_key is not None:
        return_url = update_params(return_url, return_key=return_key)

    try:
        identity_url, identity_webfinger, auth_time, sreg_res = openid_end(
            return_url=return_url,
            request=request)
    except OpenIDError as exc:
        request.session.flash(exc.message,
            level='error', icon='key--exclamation')
        return retry()

    # Find who owns this URL, if anyone
    identity_owner = model.session.query(User) \
        .filter(User.identity_urls.any(url=identity_url)) \
        .limit(1).first()

    if not identity_owner:
        if return_key:
            request.session.flash('Unknown OpenID URL.',
                level='error', icon='key--exclamation')
            return retry()
        # Someone is either registering a new account, or adding a new OpenID
        # to an existing account
        request.session['pending_identity_url'] = identity_url
        request.session.changed()

        # Try to pull a name and email address out of the SReg response
        username = re.sub(u'[^_a-z0-9]', u'',
            sreg_res.get('nickname', u'').lower())
        form = RegistrationForm(
            username=username,
            email=sreg_res.get('email', u''),
            timezone=sreg_res.get('timezone', u'UTC'),
        )
        return render_to_response(
            'account/register.mako',
            dict(
                form=form,
                identity_url=identity_url,
                identity_webfinger=identity_webfinger,
                identity_email=None),
            request=request)

    elif identity_owner == request.user:
        # Someone is just freshening up their cookie
        auth_headers = safe_openid_login(request, identity_owner, identity_url)
        if auth_headers is None:
            return retry()

        request.session.flash(u'Re-authentication successful', icon='user')

        if return_key:
            # Fetch a stashed request
            old_url = fetch_stash(request, key=return_key)['url']
            if old_url:
                location = update_params(old_url, return_key=return_key)
                log.debug('Following Return Key \'{0}\' to URL: {1}'
                          .format(return_key, location))
                return HTTPSeeOther(location, headers=auth_headers)
        return HTTPSeeOther(request.route_url('root'), headers=auth_headers)

    else:
        # Existing user; new login
        # Log the successful OpenID authentication, mindful of users that may
        # have OpenID logins disabled, for instance.
        # XXX should we deny a logged-in user to authenticate as another user?
        auth_headers = security.forget(request)
        headers = safe_openid_login(request, identity_owner, identity_url)

        if headers is None:
            return retry()

        auth_headers += headers

        # An existing user has logged in successfully.  Bravo!
        log.debug("User {0!r} logged in via OpenID: {1!r}"
                  .format(identity_owner.name, identity_url))

        request.session.flash(
            u"Welcome back, {0}!"
            .format(identity_owner.display_name or identity_owner.name),
            level=u'success', icon='user')

        # XXX this should ALSO probably do the return_key redirect, good grief
        return HTTPSeeOther(
            location=request.route_url('root'),
            headers=auth_headers)
Ejemplo n.º 20
0
    def deauth(self):
        headers = forget(self.request)

        return HTTPSeeOther(location=self.request.route_url('management',
            traverse=''), headers = headers)
Ejemplo n.º 21
0
def delete_project(project, request):
    confirm_project(project, request, fail_route="admin.project.detail")
    remove_project(project, request)

    return HTTPSeeOther(request.route_path("admin.project.list"))
Ejemplo n.º 22
0
    def registration_authorized_callback(self):
        if DEBUG_ROUTE:
            print(
                "application:flow-register:oauth1:authorized-callback",
                get_csrf_token(self.request),
            )
        if LOG_ROUTE:
            log.debug(
                "application:flow-register:oauth1:authorized-callback %s",
                get_csrf_token(self.request),
            )

        if DEBUG_USERID:
            print(
                "application:account:register:oauth1:authorized-callback",
                self.request.active_useraccount_id,
            )

        # we don't have a UID here because we haven't had an account created yet!
        if self.request.active_useraccount_id:
            return HTTPSeeOther("/application/account/home")

        public_token = self.request.params.get("oauth_token")
        public_verifier = self.request.params.get("oauth_verifier")

        oauth_sessiondata = self.request.session["3rdparty-app_oauth"]
        if not oauth_sessiondata:
            raise ApiError(
                "we could not link your authorization session correctly.")
        _oauth_token = oauth_sessiondata.get("oauth_token")
        _oauth_token_secret = oauth_sessiondata.get("oauth_token_secret")
        if (_oauth_token is None) or (_oauth_token_secret is None):
            raise ApiError(
                "we could not link your authorization session data correctly.")

        app_data = get_ApiExampleAppData()
        apiClient = CustomApiClient(
            app_key=app_data["client_key"],
            app_secret=app_data["client_secret"],
            oauth_token=_oauth_token,
            oauth_token_secret=_oauth_token_secret,
            client_args={"verify": False},
        )
        authorized = apiClient.get_authorized_tokens(public_verifier)

        # wow ok, we have successfully authorized, so...
        # log them in as a new user!
        # do this first, because the `Developer_oAuth1Server_TokenClient` requires a
        self.request.session[
            "active_useraccount_id"] = USERID_ACTIVE__APPLICATION

        newGrant = Developer_oAuth1Client_TokenAccess()
        newGrant.developer_application_id = app_data["id"]
        newGrant.useraccount_id = self.request.active_useraccount_id
        newGrant.timestamp_created = self.request.datetime
        newGrant.oauth_token = authorized["oauth_token"]
        newGrant.oauth_token_secret = authorized["oauth_token_secret"]
        newGrant._realms = (authorized["oauth_authorized_realms"]
                            if "oauth_authorized_realms" in authorized else "")
        newGrant.oauth_version = "1"
        self.request.dbSession.add(newGrant)

        return HTTPSeeOther(
            location="/application/flow-register/authorized-callback-success")
Ejemplo n.º 23
0
def reset_password(request, _form_class=ResetPasswordForm):
    if request.authenticated_userid is not None:
        return HTTPSeeOther(request.route_path("index"))

    user_service = request.find_service(IUserService, context=None)
    breach_service = request.find_service(IPasswordBreachedService,
                                          context=None)
    token_service = request.find_service(ITokenService, name="password")

    def _error(message):
        request.session.flash(message, queue="error")
        return HTTPSeeOther(
            request.route_path("accounts.request-password-reset"))

    try:
        token = request.params.get("token")
        data = token_service.loads(token)
    except TokenExpired:
        return _error("Expired token: request a new password reset link")
    except TokenInvalid:
        return _error("Invalid token: request a new password reset link")
    except TokenMissing:
        return _error("Invalid token: no token supplied")

    # Check whether this token is being used correctly
    if data.get("action") != "password-reset":
        return _error("Invalid token: not a password reset token")

    # Check whether a user with the given user ID exists
    user = user_service.get_user(uuid.UUID(data.get("user.id")))
    if user is None:
        return _error("Invalid token: user not found")

    # Check whether the user has logged in since the token was created
    last_login = data.get("user.last_login")
    if str(user.last_login) > last_login:
        # TODO: track and audit this, seems alertable
        return _error(
            "Invalid token: user has logged in since this token was requested")

    # Check whether the password has been changed since the token was created
    password_date = data.get("user.password_date")
    if str(user.password_date) > password_date:
        return _error(
            "Invalid token: password has already been changed since this "
            "token was requested")

    form = _form_class(
        **request.params,
        username=user.username,
        full_name=user.name,
        email=user.email,
        user_service=user_service,
        breach_service=breach_service,
    )

    if request.method == "POST" and form.validate():
        # Update password.
        user_service.update_user(user.id, password=form.new_password.data)
        user_service.record_event(user.id,
                                  tag="account:password:reset",
                                  ip_address=request.remote_addr)

        # Flash a success message
        request.session.flash("You have reset your password", queue="success")

        # Redirect to account login.
        return HTTPSeeOther(request.route_path("accounts.login"))

    return {"form": form}
Ejemplo n.º 24
0
def control_createfolder_(request):
    form = request.web_input(title="", parentid="")

    folder.create(request.userid, form)
    raise HTTPSeeOther(location="/manage/folders")
Ejemplo n.º 25
0
 def _error(message):
     request.session.flash(message, queue="error")
     return HTTPSeeOther(request.route_path("manage.account"))
Ejemplo n.º 26
0
def control_renamefolder_(request):
    form = request.web_input(folderid="", title="")

    if define.get_int(form.folderid):
        folder.rename(request.userid, form)
    raise HTTPSeeOther(location="/manage/folders")
Ejemplo n.º 27
0
def home_view(request):
    """The main view on a discussion"""
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        # need to pass the route to go to *after* login as well

        # With regards to a next_view, if explicitly stated, then
        # that is the next view. If not stated, the referer takes
        # precedence. In case of failure, login redirects to the
        # discussion which is its context.
        next_view = request.params.get('next', None)
        if not next_view and discussion:
            # If referred here from a post url, want to be able to
            # send the user back. Usually, Assembl will send the user
            # here to login on private discussions.
            referrer = request.url
            next_view = path_qs(referrer)

        login_url = get_social_autologin(request, discussion, next_view)
        if login_url:
            pass
        elif next_view:
            login_url = request.route_url("contextual_login",
                                          discussion_slug=discussion.slug,
                                          _query={"next": next_view})
        else:
            login_url = request.route_url('contextual_login',
                                          discussion_slug=discussion.slug)
        return HTTPTemporaryRedirect(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        # Would use render_to_response, except for the 401
        from pyramid_jinja2 import IJinja2Environment
        jinja_env = request.registry.queryUtility(IJinja2Environment,
                                                  name='.jinja2')
        template = jinja_env.get_template('cannot_read_discussion.jinja2')
        body = template.render(get_default_context(request))
        return Response(body, 401)

    # if the route asks for a post, get post content (because this is needed for meta tags)
    route_name = request.matched_route.name
    if route_name == "purl_posts":
        post_id = FrontendUrls.getRequestedPostId(request)
        if not post_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        post = Post.get_instance(post_id)
        if not post or post.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['post'] = post
    elif route_name == "purl_idea":
        idea_id = FrontendUrls.getRequestedIdeaId(request)
        if not idea_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        idea = Idea.get_instance(idea_id)
        if not idea or idea.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['idea'] = idea

    canAddExtract = user_has_permission(discussion.id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    preferences = discussion.preferences
    session = discussion.db
    if user_id != Everyone:
        from assembl.models import UserPreferenceCollection
        user = User.get(user_id)
        preferences = UserPreferenceCollection(user_id, discussion)
        target_locale = get_locale_from_request(request, session, user)
    else:
        target_locale = get_locale_from_request(request, session)

    translation_service_data = {}
    try:
        service = discussion.translation_service()
        if service:
            translation_service_data = service.serviceData()
    except:
        pass
    context['translation_service_data_json'] = json.dumps(
        translation_service_data)
    locale_labels = json.dumps(
        DummyGoogleTranslationService.target_locale_labels_cls(target_locale))
    context['translation_locale_names_json'] = locale_labels

    context['preferences_json'] = json.dumps(dict(preferences))
    role_names = [x for (x) in session.query(Role.name).all()]
    context['role_names'] = json.dumps(role_names)

    response = render_to_response('../../templates/index.jinja2',
                                  context,
                                  request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Ejemplo n.º 28
0
def control_unignoreuser_(request):
    form = request.web_input(username="")

    ignoreuser.remove(request.userid, define.get_userid_list(form.username))
    raise HTTPSeeOther(location="/manage/ignore")
Ejemplo n.º 29
0
def new_machine(request):
    """
    Handler for ``/{org}/machine/new/{id}``.

    The user must be authenticated for the organisation in the URL to reach here.

    ``{id}`` is the id of the catalogue item to use for the new machine.

    GET request
        Show a form to gather information required for provisioning.

    POST request
        Attempt to provision a machine with the given details.

        If the provisioning is successful, redirect the user to ``/{org}/machines``
        with a success message.

        If the provisioning fails with an error that the user can correct, show
        the form with an error message.

        If the provisioning fails with a cloud error, show an error on ``/{org}/machines``.
    """
    # Try to load the catalogue item
    item = request.active_cloud_session.get_image(request.matchdict['id'])
    # If we have a POST request, try and provision a machine with the info
    if request.method == 'POST':
        # For a POST request, the request must pass a CSRF test
        check_csrf_token(request)
        machine_info = {
            'template': item,
            'name': request.params.get('name', ''),
            'description': request.params.get('description', ''),
            'expose': request.params.get('expose', 'false'),
            'ssh_key': request.params.get('ssh_key', ''),
            'errors': {}
        }
        # Check that the SSH key is valid
        try:
            machine_info['ssh_key'] = validate_ssh_key(machine_info['ssh_key'])
        except ValueError as e:
            request.session.flash('There are errors with one or more fields',
                                  'error')
            machine_info['errors']['ssh_key'] = [str(e)]
            return machine_info
        try:
            machine = request.active_cloud_session.provision_machine(
                item.id, machine_info['name'], machine_info['description'],
                machine_info['ssh_key'], machine_info['expose'] == 'true')
            request.session.flash('Machine provisioned successfully',
                                  'success')
            if machine.external_ip:
                request.session.flash('Inbound access from internet enabled',
                                      'success')
        except cloudservices.DuplicateNameError:
            # If there is an error with a duplicate name, the user can correct that
            request.session.flash('There are errors with one or more fields',
                                  'error')
            machine_info['errors']['name'] = ['Machine name is already in use']
            return machine_info
        except cloudservices.NetworkingError:
            # Networking doesn't happen until the machine has been provisioned
            # So we report that provisioning was successful before propagating
            request.session.flash('Machine provisioned successfully',
                                  'success')
            raise
        # If we get this far, redirect to machines
        return HTTPSeeOther(location=request.route_url('machines'))
    # Only get requests should get this far
    return {
        'template': item,
        'name': '',
        'description': '',
        # The default value for expose is based on the NAT policy
        'expose': 'true' if item.nat_policy == NATPolicy.ALWAYS else 'false',
        # Use the current user's SSH key as the default
        'ssh_key': request.authenticated_user.ssh_key or '',
        'errors': {}
    }
Ejemplo n.º 30
0
def control_tagrestrictions_post_(request):
    tags = searchtag.parse_restricted_tags(request.params["tags"])
    searchtag.edit_user_tag_restrictions(request.userid, tags)

    raise HTTPSeeOther(location=request.route_path('control_tagrestrictions'))
Ejemplo n.º 31
0
    def delete_project_release(self):
        if self.request.flags.enabled(AdminFlagValue.DISALLOW_DELETION):
            self.request.session.flash(
                ("Project deletion temporarily disabled. "
                 "See https://pypi.org/help#admin-intervention for details."),
                queue="error",
            )
            return HTTPSeeOther(
                self.request.route_path(
                    "manage.project.release",
                    project_name=self.release.project.name,
                    version=self.release.version,
                ))

        version = self.request.POST.get("confirm_version")
        if not version:
            self.request.session.flash("Confirm the request", queue="error")
            return HTTPSeeOther(
                self.request.route_path(
                    "manage.project.release",
                    project_name=self.release.project.name,
                    version=self.release.version,
                ))

        if version != self.release.version:
            self.request.session.flash(
                "Could not delete release - " +
                f"{version!r} is not the same as {self.release.version!r}",
                queue="error",
            )
            return HTTPSeeOther(
                self.request.route_path(
                    "manage.project.release",
                    project_name=self.release.project.name,
                    version=self.release.version,
                ))

        self.request.db.add(
            JournalEntry(
                name=self.release.project.name,
                action="remove release",
                version=self.release.version,
                submitted_by=self.request.user,
                submitted_from=self.request.remote_addr,
            ))

        self.release.project.record_event(
            tag="project:release:remove",
            ip_address=self.request.remote_addr,
            additional={
                "submitted_by": self.request.user.username,
                "canonical_version": self.release.canonical_version,
            },
        )

        self.request.db.delete(self.release)

        self.request.session.flash(f"Deleted release {self.release.version!r}",
                                   queue="success")

        return HTTPSeeOther(
            self.request.route_path("manage.project.releases",
                                    project_name=self.release.project.name))
Ejemplo n.º 32
0
def manage_avatar_post_(request):
    form = request.web_input(image="", x1=0, y1=0, x2=0, y2=0)

    avatar.create(request.userid, form.x1, form.y1, form.x2, form.y2)
    raise HTTPSeeOther(location="/control")
Ejemplo n.º 33
0
def manage_banner_post_(request):
    form = request.web_input(image="")

    banner.upload(request.userid, form.image)
    raise HTTPSeeOther(location="/control")
Ejemplo n.º 34
0
Archivo: auth.py Proyecto: silky/floof
def persona_add_noxhr(context, request):
    return HTTPSeeOther(location=persona_add(context, request))
Ejemplo n.º 35
0
def set_lang(request):
    new_lang = request.POST['lang']
    response = HTTPSeeOther(request.route_url('preferences'))
    cookie_path = '/'  # FIXME: not necessarily
    response.set_cookie(LOCALE_COOKIE_NAME, new_lang, path=cookie_path)
    return response
Ejemplo n.º 36
0
def home_view(request):
    user_id = authenticated_userid(request) or Everyone
    context = get_default_context(request)
    discussion = context["discussion"]
    request.session["discussion"] = discussion.slug
    canRead = user_has_permission(discussion.id, user_id, P_READ)
    if not canRead and user_id == Everyone:
        # User isn't logged-in and discussion isn't public:
        # redirect to login page
        login_url = request.route_url('contextual_login',
                                      discussion_slug=discussion.slug)
        return HTTPSeeOther(login_url)
    elif not canRead:
        # User is logged-in but doesn't have access to the discussion
        return HTTPUnauthorized()

    # if the route asks for a post, get post content (because this is needed for meta tags)
    route_name = request.matched_route.name
    if route_name == "purl_posts":
        post_id = FrontendUrls.getRequestedPostId(request)
        if not post_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        post = Post.get_instance(post_id)
        if not post or post.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['post'] = post
    elif route_name == "purl_idea":
        idea_id = FrontendUrls.getRequestedIdeaId(request)
        if not idea_id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        idea = Idea.get_instance(idea_id)
        if not idea or idea.discussion_id != discussion.id:
            return HTTPSeeOther(
                request.route_url('home', discussion_slug=discussion.slug))
        context['idea'] = idea

    canAddExtract = user_has_permission(discussion.id, user_id, P_ADD_EXTRACT)
    context['canAddExtract'] = canAddExtract
    context['canDisplayTabs'] = True
    if user_id != Everyone:
        from assembl.models import AgentProfile
        user = AgentProfile.get(user_id)
        # TODO: user may not exist. Case of session with BD change.
        user.is_visiting_discussion(discussion.id)
        session = Discussion.default_db
        current_prefs = session.query(UserLanguagePreference).\
            filter_by(user_id = user_id).all()
        user = session.query(User).filter_by(id=user_id).first()

        def validate_locale(l):
            return ensure_locale_has_country(to_posix_format(locale))

        if '_LOCALE_' in request.cookies:
            locale = request.cookies['_LOCALE_']
            posix_locale = validate_locale(locale)
            process_locale(posix_locale, user_id, current_prefs, session,
                           LanguagePreferenceOrder.Cookie)

        elif '_LOCALE_' in request.params:
            locale = request.params['_LOCALE_']
            posix_locale = validate_locale(locale)
            process_locale(posix_locale, user_id, current_prefs, session,
                           LanguagePreferenceOrder.Parameter)
        else:
            locale = default_locale_negotiator(request)
            posix_locale = validate_locale(locale)
            process_locale(posix_locale, user_id, current_prefs, session,
                           LanguagePreferenceOrder.OS_Default)

    response = render_to_response('../../templates/index.jinja2',
                                  context,
                                  request=request)
    # Prevent caching the home, especially for proper login/logout
    response.cache_control.max_age = 0
    response.cache_control.prevent_auto = True
    return response
Ejemplo n.º 37
0
def manage_alias_post_(request):
    form = request.web_input(username="")

    useralias.set(request.userid, define.get_sysname(form.username))
    raise HTTPSeeOther(location="/control")
Ejemplo n.º 38
0
def reset_password(request, _form_class=ResetPasswordForm):
    if request.authenticated_userid is not None:
        return HTTPSeeOther(request.route_path("index"))

    user_service = request.find_service(IUserService, context=None)
    breach_service = request.find_service(IPasswordBreachedService,
                                          context=None)
    token_service = request.find_service(ITokenService, name="password")

    def _error(message):
        request.session.flash(message, queue="error")
        return HTTPSeeOther(
            request.route_path("accounts.request-password-reset"))

    try:
        token = request.params.get("token")
        data = token_service.loads(token)
    except TokenExpired:
        return _error(
            request._("Expired token: request a new password reset link"))
    except TokenInvalid:
        return _error(
            request._("Invalid token: request a new password reset link"))
    except TokenMissing:
        return _error(request._("Invalid token: no token supplied"))

    # Check whether this token is being used correctly
    if data.get("action") != "password-reset":
        return _error(request._("Invalid token: not a password reset token"))

    # Check whether a user with the given user ID exists
    user = user_service.get_user(uuid.UUID(data.get("user.id")))
    if user is None:
        return _error(request._("Invalid token: user not found"))

    # Check whether the user has logged in since the token was created
    last_login = datetime.datetime.fromisoformat(data.get("user.last_login"))
    # Before updating itsdangerous to 2.x the last_login was naive,
    # now it's localized to UTC
    if not last_login.tzinfo:
        last_login = pytz.UTC.localize(last_login)
    if user.last_login > last_login:
        # TODO: track and audit this, seems alertable
        return _error(
            request.
            _("Invalid token: user has logged in since this token was requested"
              ))

    # Check whether the password has been changed since the token was created
    password_date = datetime.datetime.fromisoformat(
        data.get("user.password_date"))
    # Before updating itsdangerous to 2.x the password_date was naive,
    # now it's localized to UTC
    if not password_date.tzinfo:
        password_date = pytz.UTC.localize(password_date)
    if user.password_date > password_date:
        return _error(
            request._(
                "Invalid token: password has already been changed since this "
                "token was requested"))

    form = _form_class(
        **request.params,
        username=user.username,
        full_name=user.name,
        email=user.email,
        user_service=user_service,
        breach_service=breach_service,
    )

    if request.method == "POST" and form.validate():
        password_reset_limiter = request.find_service(IRateLimiter,
                                                      name="password.reset")
        # Update password.
        user_service.update_user(user.id, password=form.new_password.data)
        user_service.record_event(user.id, tag="account:password:reset")
        password_reset_limiter.clear(user.id)

        # Send password change email
        send_password_change_email(request, user)

        # Flash a success message
        request.session.flash(request._("You have reset your password"),
                              queue="success")

        # Redirect to account login.
        return HTTPSeeOther(request.route_path("accounts.login"))

    return {"form": form}