def _is_safe_redirect(req, url):
     return is_safe_login_or_logout_redirect(
         redirect_to=url,
         request_host=req.get_host(),
         dot_client_id=req.GET.get('client_id'),
         require_https=req.is_secure(),
     )
Exemple #2
0
    def get(self, request):
        import base64
        from openedx.core.djangoapps.user_authn.utils import is_safe_login_or_logout_redirect

        ticket = request.GET.get('ticket')
        redirect_url = base64.b64decode(
            request.GET.get(
                'next', "Lw==")).decode('utf-8')
        if not is_safe_login_or_logout_redirect(redirect_url, request.get_host(), None, False):
            redirect_url = "/"
        error_url = reverse('uchileedxlogin-login:login')

        if ticket is None:
            logger.exception("error ticket")
            return HttpResponseRedirect(
                '{}?next={}'.format(
                    error_url, redirect_url))

        username = self.verify_state(request, ticket)
        if username is None:
            logger.exception("Error username ")
            return HttpResponseRedirect(
                '{}?next={}'.format(
                    error_url, redirect_url))
        try:
            self.login_user(request, username)
        except Exception:
            logger.exception("Error logging " + username + " - " + ticket)
            return HttpResponseRedirect(
                '{}?next={}'.format(
                    error_url, redirect_url))
        return HttpResponseRedirect(redirect_url)
Exemple #3
0
    def target(self):
        """
        If a redirect_url is specified in the querystring for this request, and the value is a safe
        url for redirect, the view will redirect to this page after rendering the template.
        If it is not specified, we will use the default target url.
        """
        target_url = self.request.GET.get(
            'redirect_url') or self.request.GET.get('next')

        #  Some third party apps do not build URLs correctly and send next query param without URL-encoding, resulting
        #  all plus('+') signs interpreted as space(' ') in the process of URL-decoding
        #  for example if we hit on:
        #  >> http://example.com/logout?next=/courses/course-v1:ARTS+D1+2018_T/course/
        #  we will receive in request.GET['next']
        #  >> /courses/course-v1:ARTS D1 2018_T/course/
        #  instead of
        #  >> /courses/course-v1:ARTS+D1+2018_T/course/
        #  to handle this scenario we need to encode our URL using quote_plus and then unquote it again.
        if target_url:
            target_url = parse.unquote(parse.quote_plus(target_url))

        use_target_url = target_url and is_safe_login_or_logout_redirect(
            redirect_to=target_url,
            request_host=self.request.get_host(),
            dot_client_id=self.request.GET.get('client_id'),
            require_https=self.request.is_secure(),
        )
        return target_url if use_target_url else self.default_target
Exemple #4
0
def _get_redirect_to(request):
    """
    Determine the redirect url and return if safe
    :argument
        request: request object

    :returns: redirect url if safe else None
    """
    redirect_to = request.GET.get('next')
    header_accept = request.META.get('HTTP_ACCEPT', '')

    # If we get a redirect parameter, make sure it's safe i.e. not redirecting outside our domain.
    # Also make sure that it is not redirecting to a static asset and redirected page is web page
    # not a static file. As allowing assets to be pointed to by "next" allows 3rd party sites to
    # get information about a user on edx.org. In any such case drop the parameter.
    if redirect_to:
        mime_type, _ = mimetypes.guess_type(redirect_to, strict=False)
        if not is_safe_login_or_logout_redirect(request, redirect_to):
            log.warning(
                u"Unsafe redirect parameter detected after login page: '%(redirect_to)s'",
                {"redirect_to": redirect_to}
            )
            redirect_to = None
        elif 'text/html' not in header_accept:
            log.info(
                u"Redirect to non html content '%(content_type)s' detected from '%(user_agent)s'"
                u" after login page: '%(redirect_to)s'",
                {
                    "redirect_to": redirect_to, "content_type": header_accept,
                    "user_agent": request.META.get('HTTP_USER_AGENT', '')
                }
            )
            redirect_to = None
        elif mime_type:
            log.warning(
                u"Redirect to url path with specified filed type '%(mime_type)s' not allowed: '%(redirect_to)s'",
                {"redirect_to": redirect_to, "mime_type": mime_type}
            )
            redirect_to = None
        elif settings.STATIC_URL in redirect_to:
            log.warning(
                u"Redirect to static content detected after login page: '%(redirect_to)s'",
                {"redirect_to": redirect_to}
            )
            redirect_to = None
        else:
            themes = get_themes()
            next_path = six.moves.urllib.parse.urlparse(redirect_to).path
            for theme in themes:
                if theme.theme_dir_name in next_path:
                    log.warning(
                        u"Redirect to theme content detected after login page: '%(redirect_to)s'",
                        {"redirect_to": redirect_to}
                    )
                    redirect_to = None
                    break

    return redirect_to
Exemple #5
0
def _get_redirect_to(request):
    """
    Determine the redirect url and return if safe
    :argument
        request: request object

    :returns: redirect url if safe else None
    """
    redirect_to = request.GET.get('next')
    header_accept = request.META.get('HTTP_ACCEPT', '')

    # If we get a redirect parameter, make sure it's safe i.e. not redirecting outside our domain.
    # Also make sure that it is not redirecting to a static asset and redirected page is web page
    # not a static file. As allowing assets to be pointed to by "next" allows 3rd party sites to
    # get information about a user on edx.org. In any such case drop the parameter.
    if redirect_to:
        mime_type, _ = mimetypes.guess_type(redirect_to, strict=False)
        if not is_safe_login_or_logout_redirect(request, redirect_to):
            log.warning(
                u'Unsafe redirect parameter detected after login page: %(redirect_to)r',
                {"redirect_to": redirect_to}
            )
            redirect_to = None
        elif 'text/html' not in header_accept:
            log.info(
                u'Redirect to non html content %(content_type)r detected from %(user_agent)r'
                u' after login page: %(redirect_to)r',
                {
                    "redirect_to": redirect_to, "content_type": header_accept,
                    "user_agent": request.META.get('HTTP_USER_AGENT', '')
                }
            )
            redirect_to = None
        elif mime_type:
            log.warning(
                u'Redirect to url path with specified filed type %(mime_type)r not allowed: %(redirect_to)r',
                {"redirect_to": redirect_to, "mime_type": mime_type}
            )
            redirect_to = None
        elif settings.STATIC_URL in redirect_to:
            log.warning(
                u'Redirect to static content detected after login page: %(redirect_to)r',
                {"redirect_to": redirect_to}
            )
            redirect_to = None
        else:
            themes = get_themes()
            next_path = urlparse.urlparse(redirect_to).path
            for theme in themes:
                if theme.theme_dir_name in next_path:
                    log.warning(
                        u'Redirect to theme content detected after login page: %(redirect_to)r',
                        {"redirect_to": redirect_to}
                    )
                    redirect_to = None
                    break

    return redirect_to
Exemple #6
0
 def test_safe_redirect_oauth2(self, client_redirect_uri, redirect_url, host, expected_is_safe):
     """ Test safe redirect_url parameter when logging out OAuth2 client. """
     application = ApplicationFactory(redirect_uris=client_redirect_uri)
     params = {
         'client_id': application.client_id,
         'redirect_url': redirect_url,
     }
     req = self.request.get('/logout?{}'.format(urlencode(params)), HTTP_HOST=host)
     actual_is_safe = is_safe_login_or_logout_redirect(req, redirect_url)
     self.assertEqual(actual_is_safe, expected_is_safe)
Exemple #7
0
 def test_safe_redirect_oauth2(self, client_redirect_uri, redirect_url, host, expected_is_safe):
     """ Test safe redirect_url parameter when logging out OAuth2 client. """
     application = ApplicationFactory(redirect_uris=client_redirect_uri)
     params = {
         'client_id': application.client_id,
         'redirect_url': redirect_url,
     }
     req = self.request.get('/logout?{}'.format(urlencode(params)), HTTP_HOST=host)
     actual_is_safe = is_safe_login_or_logout_redirect(req, redirect_url)
     self.assertEqual(actual_is_safe, expected_is_safe)
Exemple #8
0
    def target(self):
        """
        If a redirect_url is specified in the querystring for this request, and the value is a safe
        url for redirect, the view will redirect to this page after rendering the template.
        If it is not specified, we will use the default target url.
        """
        target_url = self.request.GET.get('redirect_url') or self.request.GET.get('next')

        if target_url and is_safe_login_or_logout_redirect(self.request, target_url):
            return target_url
        else:
            return self.default_target
Exemple #9
0
def ensure_redirect_url_is_safe(strategy, *args, **kwargs):
    """
    Ensure that the redirect url is save if a user logs in or registers by
    directly hitting the TPA url i.e /auth/login/backend_name?next=<redirect_to>

    Check it against the LOGIN_REDIRECT_WHITELIST. If it is not safe then
    redirect to SOCIAL_AUTH_LOGIN_REDIRECT_URL (defaults to /dashboard)
    """
    redirect_to = strategy.session_get(REDIRECT_FIELD_NAME, None)
    request = strategy.request

    if redirect_to and request:
        is_safe = is_safe_login_or_logout_redirect(
            redirect_to=redirect_to,
            request_host=request.get_host(),
            dot_client_id=None,
            require_https=request.is_secure(),
        )

        if not is_safe:
            safe_redirect_url = getattr(settings,
                                        'SOCIAL_AUTH_LOGIN_REDIRECT_URL',
                                        '/dashboard')
            strategy.session_set(REDIRECT_FIELD_NAME, safe_redirect_url)
Exemple #10
0
    def target(self):
        """
        If a redirect_url is specified in the querystring for this request, and the value is a safe
        url for redirect, the view will redirect to this page after rendering the template.
        If it is not specified, we will use the default target url.
        """
        target_url = self.request.GET.get('redirect_url') or self.request.GET.get('next')

        #  Some third party apps do not build URLs correctly and send next query param without URL-encoding, resulting
        #  all plus('+') signs interpreted as space(' ') in the process of URL-decoding
        #  for example if we hit on:
        #  >> http://example.com/logout?next=/courses/course-v1:ARTS+D1+2018_T/course/
        #  we will receive in request.GET['next']
        #  >> /courses/course-v1:ARTS D1 2018_T/course/
        #  instead of
        #  >> /courses/course-v1:ARTS+D1+2018_T/course/
        #  to handle this scenario we need to encode our URL using quote_plus and then unquote it again.
        if target_url:
            target_url = urllib.unquote(urllib.quote_plus(target_url))

        if target_url and is_safe_login_or_logout_redirect(self.request, target_url):
            return target_url
        else:
            return self.default_target
Exemple #11
0
def _get_redirect_to(request_host, request_headers, request_params,
                     request_is_https):
    """
    Determine the redirect url and return if safe

    Arguments:
        request_host (str)
        request_headers (dict)
        request_params (QueryDict)
        request_is_https (bool)

    Returns: str
        redirect url if safe else None
    """
    redirect_to = request_params.get('next')
    header_accept = request_headers.get('HTTP_ACCEPT', '')
    accepts_text_html = any(mime_type in header_accept
                            for mime_type in {'*/*', 'text/*', 'text/html'})

    # If we get a redirect parameter, make sure it's safe i.e. not redirecting outside our domain.
    # Also make sure that it is not redirecting to a static asset and redirected page is web page
    # not a static file. As allowing assets to be pointed to by "next" allows 3rd party sites to
    # get information about a user on edx.org. In any such case drop the parameter.
    if redirect_to:
        mime_type, _ = mimetypes.guess_type(redirect_to, strict=False)
        safe_redirect = is_safe_login_or_logout_redirect(
            redirect_to=redirect_to,
            request_host=request_host,
            dot_client_id=request_params.get('client_id'),
            require_https=request_is_https,
        )
        if not safe_redirect:
            log.warning(
                u"Unsafe redirect parameter detected after login page: '%(redirect_to)s'",
                {"redirect_to": redirect_to})
            redirect_to = None
        elif not accepts_text_html:
            log.info(
                u"Redirect to non html content '%(content_type)s' detected from '%(user_agent)s'"
                u" after login page: '%(redirect_to)s'", {
                    "redirect_to": redirect_to,
                    "content_type": header_accept,
                    "user_agent": request_headers.get('HTTP_USER_AGENT', '')
                })
            redirect_to = None
        elif mime_type:
            log.warning(
                u"Redirect to url path with specified filed type '%(mime_type)s' not allowed: '%(redirect_to)s'",
                {
                    "redirect_to": redirect_to,
                    "mime_type": mime_type
                })
            redirect_to = None
        elif settings.STATIC_URL in redirect_to:
            log.warning(
                u"Redirect to static content detected after login page: '%(redirect_to)s'",
                {"redirect_to": redirect_to})
            redirect_to = None
        else:
            themes = get_themes()
            next_path = urllib.parse.urlparse(redirect_to).path
            for theme in themes:
                if theme.theme_dir_name in next_path:
                    log.warning(
                        u"Redirect to theme content detected after login page: '%(redirect_to)s'",
                        {"redirect_to": redirect_to})
                    redirect_to = None
                    break

    return redirect_to
Exemple #12
0
 def test_safe_redirect(self, url, host, req_is_secure, expected_is_safe):
     """ Test safe next parameter """
     req = self.request.get('/login', HTTP_HOST=host)
     req.is_secure = lambda: req_is_secure
     actual_is_safe = is_safe_login_or_logout_redirect(req, url)
     self.assertEqual(actual_is_safe, expected_is_safe)
 def test_safe_redirect(self, url, host, expected_is_safe):
     """ Test safe next parameter """
     req = self.request.get('/login', HTTP_HOST=host)
     actual_is_safe = is_safe_login_or_logout_redirect(req, url)
     self.assertEqual(actual_is_safe, expected_is_safe)