def test_redirect_request(self): """The redirect request has the right arguments & generates a valid session cookie.""" req = Mock(spec=["cookies"]) req.cookies = [] url = self.get_success( self.provider.handle_redirect_request(req, b"http://client/redirect")) url = urlparse(url) auth_endpoint = urlparse(AUTHORIZATION_ENDPOINT) self.assertEqual(url.scheme, auth_endpoint.scheme) self.assertEqual(url.netloc, auth_endpoint.netloc) self.assertEqual(url.path, auth_endpoint.path) params = parse_qs(url.query) self.assertEqual(params["redirect_uri"], [CALLBACK_URL]) self.assertEqual(params["response_type"], ["code"]) self.assertEqual(params["scope"], [" ".join(SCOPES)]) self.assertEqual(params["client_id"], [CLIENT_ID]) self.assertEqual(len(params["state"]), 1) self.assertEqual(len(params["nonce"]), 1) # Check what is in the cookies self.assertEqual(len(req.cookies), 2) # two cookies cookie_header = req.cookies[0] # The cookie name and path don't really matter, just that it has to be coherent # between the callback & redirect handlers. parts = [p.strip() for p in cookie_header.split(b";")] self.assertIn(b"Path=/_synapse/client/oidc", parts) name, cookie = parts[0].split(b"=") self.assertEqual(name, b"oidc_session") macaroon = pymacaroons.Macaroon.deserialize(cookie) state = get_value_from_macaroon(macaroon, "state") nonce = get_value_from_macaroon(macaroon, "nonce") redirect = get_value_from_macaroon(macaroon, "client_redirect_url") self.assertEqual(params["state"], [state]) self.assertEqual(params["nonce"], [nonce]) self.assertEqual(redirect, "http://client/redirect")
def _parse_and_validate_macaroon(self, token: str, rights: str = "access" ) -> Tuple[str, bool]: """Takes a macaroon and tries to parse and validate it. This is cached if and only if rights == access and there isn't an expiry. On invalid macaroon raises _InvalidMacaroonException Returns: (user_id, is_guest) """ if rights == "access": cached = self.token_cache.get(token, None) if cached: return cached try: macaroon = pymacaroons.Macaroon.deserialize(token) except Exception: # deserialize can throw more-or-less anything # doesn't look like a macaroon: treat it as an opaque token which # must be in the database. # TODO: it would be nice to get rid of this, but apparently some # people use access tokens which aren't macaroons raise _InvalidMacaroonException() try: user_id = get_value_from_macaroon(macaroon, "user_id") guest = False for caveat in macaroon.caveats: if caveat.caveat_id == "guest = true": guest = True self.validate_macaroon(macaroon, rights, user_id=user_id) except ( pymacaroons.exceptions.MacaroonException, KeyError, TypeError, ValueError, ): raise InvalidClientTokenError("Invalid macaroon passed.") if rights == "access": self.token_cache[token] = (user_id, guest) return user_id, guest