예제 #1
0
    def post(self, request, email, path):
        """Emails login url with secret token to verify email ownership.

        Token is signed (but not encrypted) and valid only for the
        current site.

        The login url contains a path to which the user should be
        redirected after successful verification.
        """
        if email == None:
            return http.HttpResponseBadRequest('Email not set.')
        if not models.is_email_valid(email):
            return http.HttpResponseBadRequest('Email has invalid format.')
        if path is None or not url_utils.validate_redirection_target(path):
            path = '/'

        token = login_token.generate_login_token(request.site,
                                                 site_url=request.site_url,
                                                 email=email)

        params = urllib.urlencode(dict(next=path, token=token))
        url = '{0}{1}?{2}'.format(request.site_url, reverse('login'), params)
        subject = '[{0}] email verification'.format(request.site_url)
        from_email = settings.TOKEN_EMAIL_FROM
        body = (
            'Follow the link to verify your email address\n' +
            '{0}\n'.format(url) + '\n' +
            'Ignore this email if you have not requested such verification.')
        send_mail(subject, body, from_email, [email], fail_silently=False)
        return http.HttpResponseNoContent()
예제 #2
0
    def post(self, request, assertion):
        """Logs a user in (establishes a session cookie).

        Verifies BrowserID assertion and check that a user with an
        email verified by the BrowserID is known (added to users
        list).
        """
        if assertion == None:
            return http.HttpResponseBadRequest('BrowserId assertion not set.')
        try:
            user = auth.authenticate(site=request.site,
                                     site_url=request.site_url,
                                     assertion=assertion)
        except AssertionVerificationException as ex:
            logger.debug('Assertion verification failed.')
            return http.HttpResponseBadRequest(str(ex))
        if user is not None:
            auth.login(request, user)

            # Store all user data needed by Auth view in session, this
            # way, user table does not need to be queried during the
            # performance critical request (sessions are cached).
            request.session['user_id'] = user.id
            logger.debug('%s successfully logged.' % (user.email))
            return http.HttpResponseNoContent()
        else:
            # Unkown user.
            # Return not authorized because request was well formed (400
            # doesn't seem appropriate).
            return http.HttpResponseNotAuthorized()
예제 #3
0
파일: views.py 프로젝트: xeor/wwwhisper
    def post(self, request, email, path):
        """If the email owner can access the site, sends the login token.

        Token is not sent if the email owner is not allowed to access
        the site in order to avoid abusing this end point to send a
        flood of emails to unknown addresses.

        Token is signed (but not encrypted) and valid only for the
        current site.

        The login url contains a path to which the user should be
        redirected after successful verification.
        """
        if email == None:
            return http.HttpResponseBadRequest('Email not set.')
        if not models.is_email_valid(email):
            return http.HttpResponseBadRequest('Email has invalid format.')
        if path is None or not url_utils.validate_redirection_target(path):
            path = '/'

        if request.site.users.find_item_by_email(email) is None:
            # The email owner can not access the site. The token is
            # not sent, but the response is identical to the response
            # returned when the token is sent. This way it is not
            # possible to use the login form to query which emails are
            # allowed access. Such queries can still be possible by
            # response timing.
            return http.HttpResponseNoContent()

        token = login_token.generate_login_token(request.site,
                                                 site_url=request.site_url,
                                                 email=email)

        params = urllib.urlencode(dict(next=path, token=token))
        url = '{0}{1}?{2}'.format(request.site_url, reverse('login'), params)
        subject = '{0} access token'.format(request.site_url)
        body = (
            'Hello,\n\n'
            'You have requested access to {0}.\n'.format(request.site_url) +
            'Open this link to verify your email address:\n\n'
            '{0}\n\n'.format(url) +
            'If you have not requested such access, please ignore this email.\n'
            'The link is valid for the next 30 minutes and can be used once.\n'
        )
        from_email = settings.TOKEN_EMAIL_FROM
        success = False
        try:
            success = (send_mail(
                subject, body, from_email, [email], fail_silently=False) > 0)
        except Exception as ex:
            logger.warning(ex)
        if not success:
            # This probaly can be also due to invalid email address,
            # in these cases 400 would be better.
            msg = 'Email delivery problem. ' \
                'Check the entered address or try again in a few minutes.'
            return http.HttpResponseInternalError(msg)
        return http.HttpResponseNoContent()
예제 #4
0
 def process_request(self, request):
     url = request.META.get('HTTP_SITE_URL', None)
     if url is None:
         return http.HttpResponseBadRequest('Missing Site-Url header')
     url = url_utils.remove_default_port(url)
     parts = url.split('://', 1)
     if len(parts) != 2:
         return http.HttpResponseBadRequest('Site-Url has incorrect format')
     scheme, host = parts
     if not self._alias_defined(request.site, url):
         return self._site_url_invalid(request, scheme, host)
     request.site_url = url
     request.META[SECURE_PROXY_SSL_HEADER] = scheme
     request.META['HTTP_X_FORWARDED_HOST'] = host
     # TODO: use is_secure() instead
     request.https = (scheme == 'https')
     return None
예제 #5
0
 def _site_url_invalid(self, request, scheme, host):
     if self._needs_https_redirect(request.site, scheme, host):
         logger.debug('Request over http, redirecting to https')
         return redirect('https://' + host + self._get_full_path(request))
     msg = 'Invalid request URL, you can use wwwhisper admin to allow ' \
         'requests from this address.'
     logger.warning(msg)
     return http.HttpResponseBadRequest(msg)
예제 #6
0
파일: views.py 프로젝트: xeor/wwwhisper
 def put(self, request, title, header, message, branding):
     try:
         request.site.update_skin(title=title,
                                  header=header,
                                  message=message,
                                  branding=branding)
     except ValidationError as ex:
         return http.HttpResponseBadRequest(
             'Failed to update login page: ' + ', '.join(ex.messages))
     return http.HttpResponseOKJson(request.site.skin())
예제 #7
0
파일: views.py 프로젝트: xeor/wwwhisper
    def get(self, request):
        """Logs a user in (establishes a session cookie).

        Verifies a token and check that a user with an email encoded
        in the token is known.

        On success redirects to path passed in the 'next' url
        argument.
        """
        # TODO(jw): should this first check if the user is already
        # logged in and redirect to '/' if this is the case?
        token = request.GET.get('token')
        if token == None:
            return http.HttpResponseBadRequest('Token missing.')
        try:
            user = auth.authenticate(site=request.site,
                                     site_url=request.site_url,
                                     token=token)
        except AuthenticationError as ex:
            logger.debug('Token verification failed.')
            return http.HttpResponseBadRequest(str(ex))
        if user is not None:
            auth.login(request, user)
            user.login_successful()

            # Store all user data needed by Auth view in session, this
            # way, user table does not need to be queried during the
            # performance critical request (sessions are cached).
            request.session['user_id'] = user.id
            logger.debug('%s successfully logged.' % (user.email))
            redirect_to = request.GET.get('next')
            if (redirect_to is None
                    or not url_utils.validate_redirection_target(redirect_to)):
                redirect_to = '/'

            return http.HttpResponseRedirect(request.site_url + redirect_to)

        # Return not authorized because request was well formed (400
        # doesn't seem appropriate).
        return http.HttpResponseNotAuthorized(
            _html_or_none(request, 'nothing_accessible.html'))
예제 #8
0
파일: views.py 프로젝트: xeor/wwwhisper
    def post(self, request, **kwargs):
        """Ads a new resource to the collection.

        Args:
            **kwargs: holds collection dependent arguments that are
              used to create the resource.
        Returns json representation of the added resource."""
        try:
            created_item = self.collection.create_item(**kwargs)
        except ValidationError as ex:
            # ex.messages is a list of errors.
            return http.HttpResponseBadRequest(', '.join(ex.messages))
        except LimitExceeded as ex:
            return http.HttpResponseLimitExceeded(str(ex))

        attributes_dict = created_item.attributes_dict(request.site_url)
        response = http.HttpResponseCreated(attributes_dict)
        response['Location'] = attributes_dict['self']
        response['Content-Location'] = attributes_dict['self']
        return response
예제 #9
0
    def get(self, request):
        """Invoked by the HTTP server with a single path argument.

        The HTTP server should pass the path argument verbatim,
        without any transformations or decoding. Access control
        mechanism should work on user visible paths, not paths after
        internal rewrites performed by the server.

        At the moment, the path is allowed to contain a query part,
        which is ignored (this is because nginx does not expose
        encoded path without the query part).

        The method follows be conservative in what you accept
        principle. The path should be absolute and normalized, without
        fragment id, otherwise access is denied. Browsers in normal
        operations perform path normalization and do not send fragment
        id. Multiple consecutive '/' separators are permitted, because
        these are not normalized by browsers, and are used by
        legitimate applications.  Paths with '/./' and '/../', should
        not be normally sent by browsers and can be a sign of
        something suspicious happening. It is extremely important that
        wwwhisper does not perform any path transformations that are
        not be compatible with transformations done by the HTTP
        server.
       """
        encoded_path = self._extract_encoded_path_argument(request)
        if encoded_path is None:
            return http.HttpResponseBadRequest(
                "Auth request should have 'path' argument.")

        # Do not allow requests that contain the 'User' header. The
        # header is passed to backends and must be guaranteed to be
        # set by wwwhisper.
        # This check should already be performed by HTTP server.
        if 'HTTP_USER' in request.META:
            return http.HttpResponseBadRequest(
                "Client can not set the 'User' header")

        debug_msg = "Auth request to '%s'" % (encoded_path)

        path_validation_error = None
        if url_utils.contains_fragment(encoded_path):
            path_validation_error = "Path should not include fragment ('#')"
        else:
            stripped_path = url_utils.strip_query(encoded_path)
            decoded_path = url_utils.decode(stripped_path)
            decoded_path = url_utils.collapse_slashes(decoded_path)
            if not url_utils.is_canonical(decoded_path):
                path_validation_error = 'Path should be absolute and ' \
                    'normalized (starting with / without /../ or /./ or //).'
        if path_validation_error is not None:
            logger.debug('%s: incorrect path.' % (debug_msg))
            return http.HttpResponseBadRequest(path_validation_error)

        user = _get_user(request)
        location = request.site.locations.find_location(decoded_path)
        if user is not None:

            debug_msg += " by '%s'" % (user.email)
            respone = None

            if location is not None and location.can_access(user):
                logger.debug('%s: access granted.' % (debug_msg))
                response = http.HttpResponseOK('Access granted.')
            else:
                logger.debug('%s: access denied.' % (debug_msg))
                response = http.HttpResponseNotAuthorized(
                    _html_or_none(request, 'not_authorized.html',
                                  {'email': user.email}))
            response['User'] = user.email
            return response

        if (location is not None and location.open_access_granted()
                and not location.open_access_requires_login()):
            logger.debug('%s: authentication not required, access granted.' %
                         (debug_msg))
            return http.HttpResponseOK('Access granted.')
        logger.debug('%s: user not authenticated.' % (debug_msg))
        return http.HttpResponseNotAuthenticated(
            _html_or_none(request, 'login.html', request.site.skin()))