def get(self, request, *args, **kwargs): """ As a credential requestor, /login accepts up to three optional parameters: 1. ``service``: the identifier of the application the client is accessing. In most cases this will be a URL. 2. ``renew``: requires a client to present credentials regardless of any existing single sign-on session. If set, its value should be "true". 3. ``gateway``: causes the client to not be prompted for credentials. If a single sign-on session already exists, the user will be logged in. Otherwise, the user is simply forwarded to the service, if specified. If set, its value should be "true". """ service = request.GET.get('service') renew = request.GET.get('renew') gateway = request.GET.get('gateway') warned = request.GET.get('warned') if renew: logger.debug("Renew request received by credential requestor") self.logout_user(request) login = add_query_params(reverse('cas_login'), {'service': service}) logger.debug("Redirecting to %s" % login) return redirect(login) elif gateway and service: logger.debug("Gateway request received by credential requestor") if request.user.is_authenticated(): if self.warn_user() and not warned: return redirect(add_query_params(reverse('cas_warn'), {'service': service, 'gateway': gateway})) st = ServiceTicket.objects.create_ticket(service=service, user=request.user) service = add_query_params(service, {'ticket': st.ticket}) logger.debug("Redirecting to %s" % service) return redirect(service) elif request.user.is_authenticated(): if service: logger.debug("Service ticket request received " "by credential requestor") if self.warn_user() and not warned: return redirect(add_query_params(reverse('cas_warn'), {'service': service})) st = ServiceTicket.objects.create_ticket(service=service, user=request.user) service = add_query_params(service, {'ticket': st.ticket}) logger.debug("Redirecting to %s" % service) return redirect(service) else: messages.success(request, _("You are logged in as %s") % request.user) return super(LoginView, self).get(request, *args, **kwargs)
def form_valid(self, form): service = form.cleaned_data.get('service') gateway = form.cleaned_data.get('gateway') return redirect(add_query_params(reverse('cas_login'), {'service': service, 'gateway': gateway, 'warned': 'true'}))
def validate_pgturl(self, pgturl, pgtid, pgtiou): """ Verify the provided proxy callback URL. This verification process requires three steps: 1. The URL scheme must be HTTPS 2. The SSL certificate must be valid and its name must match that of the service 3. The callback URL must respond with a 200 or 3xx response code It is not required for validation that 3xx redirects be followed. """ # Ensure the scheme is HTTPS before proceeding if not is_scheme_https(pgturl): raise InternalError("Proxy callback %s is not HTTPS" % pgturl) # Connect to proxy callback URL, checking the SSL certificate pgturl = add_query_params(pgturl, {'pgtId': pgtid, 'pgtIou': pgtiou}) try: verify = os.environ.get('REQUESTS_CA_BUNDLE', True) r = requests.get(pgturl, verify=verify) except (requests.exceptions.ConnectionError, requests.exceptions.SSLError) as e: raise InternalError("Proxy callback %s returned %s" % (pgturl, e)) # Check the returned HTTP status code try: r.raise_for_status() except requests.exceptions.HTTPError as e: raise InternalError("Proxy callback %s returned %s" % (pgturl, e))
def valid_response(self, form): login(self.request, form.user) logger.info("Single sign-on session started for %s" % form.user) if form.cleaned_data.get('warn'): self.request.session['warn'] = True service = self.request.REQUEST.get('service') if service: st = ServiceTicket.objects.create_ticket(service=service, user=self.request.user, primary=True) if not is_valid_service_url(service): data = dict(state=False, error=u'permission denied') else: cross_domain_urls = get_service_login_urls_with_ticket(service, self.request.user) params = {'ticket': st.ticket} next_page = self.request.REQUEST.get('next', '') if next_page and is_valid_service_url(next_page): params['next'] = next_page redirect_to = add_query_params(service, params=params) data = dict(state=True, redirect_to=redirect_to, cross_domain_urls=cross_domain_urls) else: data = dict(state=False) return data
def test_login_view_invalid_service(self): """ When called with an invalid service URL, the view should return a 302 Redirect to the InvalidService View. """ response = self.client.get(reverse('cas_login'), {'service': self.service_url, 'gateway': 'true'}) self.assertRedirects(response, add_query_params(reverse('cas_invalid_service'), {'service': self.service_url}), target_status_code=403)
def validate_callback(self, service, pgturl, pgtid, pgtiou): """Verify the provided proxy callback URL.""" if not proxy_allowed(service): raise UnauthorizedServiceProxy("%s is not authorized to use proxy authentication" % service) if not is_scheme_https(pgturl): raise InvalidProxyCallback("Proxy callback %s is not HTTPS" % pgturl) if not proxy_callback_allowed(service, pgturl): raise InvalidProxyCallback("%s is not an authorized proxy callback URL" % pgturl) # Verify that the SSL certificate is valid verify = os.environ.get('REQUESTS_CA_BUNDLE', True) try: requests.get(pgturl, verify=verify, timeout=5) except requests.exceptions.SSLError: raise InvalidProxyCallback("SSL certificate validation failed for proxy callback %s" % pgturl) except requests.exceptions.RequestException as e: raise InvalidProxyCallback(e) # Callback certificate appears valid, so send the ticket strings pgturl = add_query_params(pgturl, {'pgtId': pgtid, 'pgtIou': pgtiou}) try: response = requests.get(pgturl, verify=verify, timeout=5) except requests.exceptions.RequestException as e: raise InvalidProxyCallback(e) try: response.raise_for_status() except requests.exceptions.HTTPError as e: raise InvalidProxyCallback("Proxy callback %s returned %s" % (pgturl, e))
def validate_callback(self, service, pgturl, pgtid, pgtiou): """Verify the provided proxy callback URL.""" if not get_config(service, 'PROXY_ALLOW'): raise UnauthorizedServiceProxy("%s is not authorized to use proxy authentication" % service) if not is_scheme_https(pgturl): raise InvalidProxyCallback("Proxy callback %s is not HTTPS" % pgturl) if not is_valid_proxy_callback(service, pgturl): raise InvalidProxyCallback("%s is not an authorized proxy callback URL" % pgturl) # Check the proxy callback URL and SSL certificate pgturl_params = add_query_params(pgturl, {'pgtId': pgtid, 'pgtIou': pgtiou}) verify = os.environ.get('REQUESTS_CA_BUNDLE', True) try: r = requests.get(pgturl_params, verify=verify, timeout=3.0) except requests.exceptions.SSLError: msg = "SSL cert validation failed for proxy callback %s" % pgturl raise InvalidProxyCallback(msg) except requests.exceptions.ConnectionError: msg = "Error connecting to proxy callback %s" % pgturl raise InvalidProxyCallback(msg) except requests.exceptions.Timeout: msg = "Timeout connecting to proxy callback %s" % pgturl raise InvalidProxyCallback(msg) # Check the returned HTTP status code try: r.raise_for_status() except requests.exceptions.HTTPError as e: msg = "Proxy callback %s returned %s" % (pgturl, e) raise InvalidProxyCallback(msg)
def get(self, request, *args, **kwargs): service = request.GET.get('service') ticket = request.GET.get('ticket') msg = _("Do you want to access %s as %s?") % (clean_service_url(service), request.user) messages.info(request, msg) kwargs['service'] = add_query_params(service, {'ticket': ticket}) return super(WarnView, self).get(request, *args, **kwargs)
def test_add_query_params_unicode(self): """ When Unicode parameters are provided, ``add_query_params()`` should encode them appropriately. """ params = {'unicode1': u'ä', u'unicode²': 'b'} url = add_query_params('http://www.example.com/', params) self.assertIn('unicode1=%C3%A4', url) self.assertIn('unicode%C2%B2=b', url)
def get(self, request, *args, **kwargs): service = request.GET.get('service') ticket = request.GET.get('ticket') if not service or not is_valid_service_url(service): return redirect('cas_login') msg = _("Do you want to access %(service)s as %(user)s?") % { 'service': clean_service_url(service), 'user': request.user} messages.info(request, msg) kwargs['service'] = add_query_params(service, {'ticket': ticket}) return super(WarnView, self).get(request, *args, **kwargs)
def get_service_login_urls_with_ticket(exclude_url, user): """ :param exclude_url: service url excluded :param user: generate `ServiceTicket` for `user` :return: MAMA_CAS_VALID_SERVICES login urls with ticket exclude `exclude_url` """ urls = [] for service in get_service_urls(exclude_url): st = ServiceTicket.objects.create_ticket(service=service, user=user) url = urljoin(service, '/accounts/login/') urls.append(add_query_params(url, params={'ticket': st.ticket})) return urls
def test_add_query_params(self): """ When called with a URL and a dict of parameters, ``add_query_params()`` should insert the parameters into the original URL. """ url = 'http://www.example.com/?test3=blue' params = {'test1': 'red', 'test2': '', 'test3': 'indigo'} url = add_query_params(url, params) self.assertIn('test1=red', url) # Parameters with empty values should be ignored self.assertNotIn('test2=', url) # Existing parameters with the same name should be overwritten self.assertIn('test3=indigo', url)
def validate_callback(self, service, pgturl, pgtid, pgtiou): """Verify the provided proxy callback URL.""" if not can_proxy_authentication(service): raise UnauthorizedServiceProxy( "%s is not authorized to use proxy authentication" % service) if not is_scheme_https(pgturl): raise InvalidProxyCallback("Proxy callback %s is not HTTPS" % pgturl) if not is_valid_proxy_callback(service, pgturl): raise InvalidProxyCallback( "%s is not an authorized proxy callback URL" % pgturl) # Check the proxy callback URL and SSL certificate pgturl_params = add_query_params(pgturl, { 'pgtId': pgtid, 'pgtIou': pgtiou }) verify = os.environ.get('REQUESTS_CA_BUNDLE', True) try: r = requests.get(pgturl_params, verify=verify, timeout=3.0) except requests.exceptions.SSLError: msg = "SSL cert validation failed for proxy callback %s" % pgturl raise InvalidProxyCallback(msg) except requests.exceptions.ConnectionError: msg = "Error connecting to proxy callback %s" % pgturl raise InvalidProxyCallback(msg) except requests.exceptions.Timeout: msg = "Timeout connecting to proxy callback %s" % pgturl raise InvalidProxyCallback(msg) # Check the returned HTTP status code try: r.raise_for_status() except requests.exceptions.HTTPError as e: msg = "Proxy callback %s returned %s" % (pgturl, e) raise InvalidProxyCallback(msg)
def form_valid(self, form): """ (2.2) As a credential acceptor, /login requires two parameters: 1. ``username``: the username provided by the client 2. ``password``: the password provided by the client If authentication is successful, the single sign-on session is created. If a service is provided, a ``ServiceTicket`` is created and the client is redirected to the service URL with the ``ServiceTicket`` included. If no service is provided, the login page is redisplayed with a message indicating a successful login. If authentication fails, the login form is redisplayed with an error message describing the reason for failure. The credential acceptor accepts one optional parameter: 1. ``warn``: causes the user to be prompted when successive authentication attempts occur within the single sign-on session. """ login(self.request, form.user) logger.info("Single sign-on session started for %s" % form.user) if form.cleaned_data.get('warn'): self.request.session['warn'] = True service = form.cleaned_data.get('service') if service: st = ServiceTicket.objects.create_ticket(service=service, user=self.request.user, primary=True) service = add_query_params(service, {'ticket': st.ticket}) logger.debug("Redirecting to %s" % service) return redirect(service) return redirect(reverse('cas_login'))
def validate_callback(self, url, pgtid, pgtiou): """ Verify the provided proxy callback URL. This verification process requires three steps: 1. The URL scheme must be HTTPS 2. The SSL certificate must be valid and its name must match that of the service 3. The callback URL must respond with a 200 or 3xx response code It is not required for validation that 3xx redirects be followed. """ # Ensure the scheme is HTTPS before proceeding if not is_scheme_https(url): raise InvalidProxyCallback("Proxy callback %s is not HTTPS" % url) # Connect to proxy callback URL, checking the SSL certificate url_params = add_query_params(url, {'pgtId': pgtid, 'pgtIou': pgtiou}) verify = os.environ.get('REQUESTS_CA_BUNDLE', True) try: r = requests.get(url_params, verify=verify, timeout=3.0) except requests.exceptions.SSLError: msg = "SSL cert validation failed for proxy callback %s" % url raise InvalidProxyCallback(msg) except requests.exceptions.ConnectionError: msg = "Error connecting to proxy callback %s" % url raise InvalidProxyCallback(msg) except requests.exceptions.Timeout: msg = "Timeout connecting to proxy callback %s" % url raise InvalidProxyCallback(msg) # Check the returned HTTP status code try: r.raise_for_status() except requests.exceptions.HTTPError as e: msg = "Proxy callback %s returned %s" % (url, e) raise InvalidProxyCallback(msg)
def form_valid(self, form): """ As a credential acceptor, /login takes two required parameters: 1. ``username``: the username provided by the client 2. ``password``: the password provided by the client If authentication is successful, the user is logged in which creates the single sign-on session. If a service is provided, a corresponding ``ServiceTicket`` is created, and the user is redirected to the service URL. If no service is provided, the user is redirected back to the login page with a message indicating a successful login. If authentication fails, the login form is redisplayed with an appropriate error message displayed indicating the reason for failure. The credential acceptor also accepts one optional parameter: 1. ``warn``: causes user input to be required whenever an authentication attempt occurs within the single sign-on session. """ auth.login(self.request, form.user) logger.info("Single sign-on session started for %s" % self.request.user) if form.cleaned_data.get('warn'): self.request.session['warn'] = True service = form.cleaned_data.get('service') if service: st = ServiceTicket.objects.create_ticket(service=service, user=self.request.user, primary=True) service = add_query_params(service, {'ticket': st.ticket}) logger.debug("Redirecting to %s" % service) return redirect(service) return redirect(reverse('cas_login'))