def render_GET(self, request: Request): # $ requestHandler request.addCookie( "key", "value") # $ CookieWrite CookieName="key" CookieValue="value" request.addCookie( k="key", v="value") # $ CookieWrite CookieName="key" CookieValue="value" val = "key2=value" request.cookies.append(val) # $ CookieWrite CookieRawHeader=val request.responseHeaders.addRawHeader("key", "value") request.setHeader( "Set-Cookie", "key3=value3" ) # $ MISSING: CookieWrite CookieRawHeader="key3=value3" return b"" # $ HttpResponse mimetype=text/html responseBody=b""
async def async_render_POST(self, request: Request): session_id = request.getCookie(SESSION_COOKIE_NAME) if not session_id: _return_html_error(400, "missing session_id", request) return session_id = session_id.decode("ascii", errors="replace") session = get_mapping_session(session_id) if not session: logger.info("Session ID %s not found", session_id) _return_html_error(403, "Unknown session", request) return # we don't clear the session from the dict until the ID is successfully # registered, so the user can go round and have another go if need be. # # this means there's theoretically a race where a single user can register # two accounts. I'm going to assume that's not a dealbreaker. if b"username" not in request.args: _return_html_error(400, "missing username", request) return localpart = request.args[b"username"][0].decode("utf-8", errors="replace") if b"password" not in request.args: logger.info("Registering username %s", localpart) try: registered_user_id = await self._module_api.register_user( localpart=localpart, displayname=localpart) except SynapseError as e: logger.warning("Error during registration: %s", e) _return_html_error(e.code, e.msg, request) return else: password = request.args[b"password"][0].decode("utf-8", errors="replace") if localpart.startswith("@"): registered_user_id = localpart else: registered_user_id = UserID( localpart, self._module_api._hs.hostname).to_string() success = False try: passwd_response = await self._module_api._auth_handler._check_local_password( registered_user_id, password) if passwd_response != registered_user_id: raise LoginError(403, "Invalid password", errcode=Codes.FORBIDDEN) except Exception as e: logger.warning("Error checking credentials of %s: %s %s" % (localpart, type(e), e)) _return_html_error(e.code, e.msg, request) return await self._module_api.record_user_external_id("saml", session.remote_user_id, registered_user_id) del username_mapping_sessions[session_id] # delete the cookie request.addCookie( SESSION_COOKIE_NAME, b"", expires=b"Thu, 01 Jan 1970 00:00:00 GMT", path=b"/", ) await self._module_api.complete_sso_login_async( registered_user_id, request, session.client_redirect_url, )
async def register_sso_user(self, request: Request, session_id: str) -> None: """Called once we have all the info we need to register a new user. Does so and serves an HTTP response Args: request: HTTP request session_id: ID of the username mapping session, extracted from a cookie """ try: session = self.get_mapping_session(session_id) except SynapseError as e: self.render_error(request, "bad_session", e.msg, code=e.code) return logger.info( "[session %s] Registering localpart %s", session_id, session.chosen_localpart, ) attributes = UserAttributes( localpart=session.chosen_localpart, emails=session.emails_to_use, ) if session.use_display_name: attributes.display_name = session.display_name # the following will raise a 400 error if the username has been taken in the # meantime. user_id = await self._register_mapped_user( attributes, session.auth_provider_id, session.remote_user_id, get_request_user_agent(request), request.getClientIP(), ) logger.info( "[session %s] Registered userid %s with attributes %s", session_id, user_id, attributes, ) # delete the mapping session and the cookie del self._username_mapping_sessions[session_id] # delete the cookie request.addCookie( USERNAME_MAPPING_SESSION_COOKIE_NAME, b"", expires=b"Thu, 01 Jan 1970 00:00:00 GMT", path=b"/", ) auth_result = {} if session.terms_accepted_version: # TODO: make this less awful. auth_result[LoginType.TERMS] = True await self._registration_handler.post_registration_actions( user_id, auth_result, access_token=None) await self._auth_handler.complete_sso_login( user_id, session.auth_provider_id, request, session.client_redirect_url, session.extra_login_attributes, new_user=True, )
def render(self, request: server.Request) -> bytes: # Deny by default. request.setResponseCode(401) # Get session cookie value if any. sessionid = request.getCookie(self.cookie) if sessionid is not None: if sessionid in self.sessions: request.setResponseCode(200) self.log.info("Session: Validation succeeded") return b"" else: self.log.info("Session: Invalid session id") # Token is passed as a query parameter in the original URL. origurl = http.urlparse(request.getHeader(self.header)) query = http.parse_qs(origurl.query) args = query.get(self.param, []) if len(args) != 1: self.log.error("Request: Token {param} missing", param=self.param) return b"" try: token = jwt.JWT(key=self.key, jwt=args[0].decode()) except (jwt.JWTExpired, jwt.JWTNotYetValid, jwt.JWTMissingClaim, jwt.JWTInvalidClaimValue, jwt.JWTInvalidClaimFormat, jwt.JWTMissingKeyID, jwt.JWTMissingKey) as error: self.log.error("JWT token: {error}", error=error) return b"" except Exception: self.log.failure("JWT token: Unknown exception") return b"" try: claims = json.loads(token.claims) except json.JSONDecodeError as error: self.log.failure("JWT token: Claims {error}", error=error) return b"" # Collect session parameters from claims. sessparams = claims.get("session", {}) kwargs = { "expires": sessparams.get("expires", None), "domain": sessparams.get("domain", None), "path": sessparams.get("path", None), "secure": sessparams.get("secure", None), "httpOnly": sessparams.get("httpOnly", None), "sameSite": sessparams.get("sameSite", None), } # Use maxAge for session ttl if it is present, convert it into a str # type as required by the addCookie call. if "maxAge" in sessparams: kwargs["max_age"] = str(sessparams["maxAge"]) sessttl = int(sessparams["maxAge"]) else: sessttl = self.sessttl # Generate a new session id and remember it. Also clean it up after # ttl seconds. sessionid = secrets.token_urlsafe(nbytes=16).encode() self.sessions.add(sessionid) reactor.callLater(sessttl, self._session_remove, sessionid) self.log.info("Session: Created, num sessions: {sessions}", sessions=len(self.sessions)) # Set cookie in the browser. request.addCookie(self.cookie, sessionid, **kwargs) request.setResponseCode(200) self.log.info("JWT token: Validation succeeded") return b""