def test_valid_redirect_url(self): url = sso.sso_redirect_url( nonce=self.nonce, secret=self.secret, email=self.email, external_id=self.external_id, username=self.username, name=self.name, avatar_url=self.avatar_url, bio=self.bio) self.assertIn('/session/sso_login', url[:20]) # check its valid, using our own handy validator params = parse_qs(urlparse(url).query) payload = params['sso'][0] sso.sso_validate(payload, params['sig'][0], self.secret) # check the params have all the data we expect payload = b64decode(payload.encode('utf-8')).decode('utf-8') payload = unquote_plus(payload) payload = dict((p.split('=') for p in payload.split('&'))) self.assertEqual(payload, { 'username': self.username, 'nonce': self.nonce, 'external_id': self.external_id, 'name': self.name, 'email': self.email, 'name': self.name, 'avatar_url': self.avatar_url, 'bio': self.bio })
def test_valid_redirect_url(self): url = sso.sso_redirect_url( self.nonce, self.secret, self.email, self.external_id, self.username, name="sam", ) self.assertIn("/session/sso_login", url[:20]) # check its valid, using our own handy validator params = parse_qs(urlparse(url).query) payload = params["sso"][0] sso.sso_validate(payload, params["sig"][0], self.secret) # check the params have all the data we expect payload = b64decode(payload.encode("utf-8")).decode("utf-8") payload = unquote(payload) payload = dict((p.split("=") for p in payload.split("&"))) self.assertEqual( payload, { "username": self.username, "nonce": self.nonce, "external_id": self.external_id, "name": self.name, "email": self.email, }, )
def discourse_sso_login(): if not main.app.config["ENABLE_SSO_FOR_DISCOURSE"]: return "Single-sign on for discourse forum is disabled by Exeris server configuration" payload = request.args["sso"] signature = request.args["sig"] sso_secret = main.app.config["DISCOURSE_SSO_SECRET"] try: sso_validate(payload, signature, sso_secret) except DiscourseError as e: return str(e.args) if g.player.confirmed_at is None: return "The email address of the {} is not yet confirmed".format(g.player.id) base64_encoded_payload = urllib.parse.unquote(payload) payload = urllib.parse.parse_qs(base64.b64decode(base64_encoded_payload).decode("utf8")) response_payload = sso_payload(sso_secret, nonce=payload["nonce"][0], email=g.player.email, external_id=g.player.id, username=g.player.id ) return redirect(payload["return_sso_url"][0] + "?" + response_payload)
def sso(request): try: payload = request.GET.get('sso') sig = request.GET.get('sig') nonce = sso_validate(payload, sig, settings.DISCOURSE_SSO_SECRET) # Workaround for bug in pydiscourse.sso: It creates a nonce which # looks like "6ec2d11456ecba485823f791a468a4cd&return_sso_url", # so just remove the &return_sso_url if '&' in nonce: nonce = nonce.split('&')[0] # FIXME Add nickname to ldap as seperate field nickname = re.sub(r'(.+)\s\((.+)\)$', r'\1', request.user.get_ldapuser().display_name) url = sso_redirect_url(nonce, settings.DISCOURSE_SSO_SECRET, request.user.email, request.user.username, request.user.username, name=nickname.encode('utf-8')) except DiscourseError: return HttpResponse(status=400) return redirect(settings.DISCOURSE_URL + url)
def test_valid_redirect_url(self): url = sso.sso_redirect_url(self.nonce, self.secret, self.email, self.external_id, self.username, name='sam') self.assertIn('/session/sso_login', url[:20]) # check its valid, using our own handy validator params = parse_qs(urlparse(url).query) payload = params['sso'][0] sso.sso_validate(payload, params['sig'][0], self.secret) # check the params have all the data we expect payload = base64.decodestring(payload) payload = unquote(payload) payload = dict((p.split('=') for p in payload.split('&'))) self.assertEqual(payload, { 'username': self.username, 'nonce': self.nonce, 'external_id': self.external_id, 'name': self.name, 'email': self.email })
def view(): if 'nonce' in request.args: nonce = request.args.get('nonce') else: payload = request.args.get('sso') signature = request.args.get('sig') try: nonce = sso_validate(payload, signature, SECRET) except DiscourseError as e: return render_template('error.html', error=e) nonce = nonce.rstrip('&return_sso_url') # IDK???!!!! return render_template('view.html', nonce=nonce)
def test_missing_args(self): with self.assertRaises(DiscourseError): sso.sso_validate(None, self.signature, self.secret) with self.assertRaises(DiscourseError): sso.sso_validate('', self.signature, self.secret) with self.assertRaises(DiscourseError): sso.sso_validate(self.payload, None, self.secret)
def perform_discourse_login(user): plugin_config = helpers.get_plugin_config('colab_discourse') prefix = helpers.get_plugin_prefix('colab_discourse', regex=False) base_url = Site.objects.get_current().domain base_url = "{}/{}".format(base_url, prefix) url = base_url + "/session/sso" response = requests.get(url, allow_redirects=False) if response.status_code == 302: location = response.headers.get("Location") regex = re.compile(r"sso=(.+)&sig=(.+)") matches = regex.search(location) payload, signature = matches.groups() secret = plugin_config.get('sso_secret') nonce = sso_validate(payload, signature, secret) url = sso_redirect_url(nonce, secret, user.email, user.id, user.username, name=user.first_name.encode('utf-8')) response = requests.get(base_url + url, allow_redirects=False) return response return None
def discourse_login(sender, user, request, **kwargs): upstream = settings.DISCOURSE_UPSTREAM upstream = upstream[:-1] if upstream[-1] == '/' else upstream response = requests.get(upstream + '/session/sso', allow_redirects=False) if response.status_code == 302: location = response.headers['Location'] regex = re.compile(r'sso=(.+)&sig=(.+)') matches = regex.search(location) payload, signature = matches.groups() secret = settings.DISCOURSE_SSO_SECRET nonce = sso_validate(payload, signature, secret) url = sso_redirect_url(nonce, secret, user.email, user.id, user.username, name=user.get_full_name().encode('utf-8')) response = requests.get(upstream + url, allow_redirects=False) t_cookie = response.cookies.get('_t') request.COOKIES['_t'] = t_cookie
def test_valid_nonce(self): nonce = sso.sso_validate(self.payload, self.signature, self.secret) self.assertEqual(nonce, self.nonce)
def test_invalid_signature(self): with self.assertRaises(DiscourseError): sso.sso_validate(self.payload, 'notavalidsignature', self.secret)