def process_response(response): if request.endpoint in ('static', 'baseframe.static'): if 'Access-Control-Allow-Origin' not in response.headers: # This is required for webfont resources # Note: We do not serve static assets in production, nginx does. # That means this piece of code will never be called in production. response.headers['Access-Control-Allow-Origin'] = '*' # If Babel was accessed in this request, the response's contents will vary with # the accepted language if ctx_has_locale(): response.vary.add('Accept-Language') # If current_auth was accessed during this request, it is sensitive to the lastuser # cookie if request_has_auth(): response.vary.add('Cookie') # Prevent pages from being placed in an iframe. If the response already # set has a value for this option, let it pass through if 'X-Frame-Options' in response.headers: frameoptions = response.headers.get('X-Frame-Options') if not frameoptions or frameoptions == 'ALLOW': # 'ALLOW' is an unofficial signal from the app to Baseframe. # It signals us to remove the header and not set a default response.headers.pop('X-Frame-Options') else: if request_has_auth() and getattr(current_auth, 'login_required', False): # Protect only login_required pages from appearing in frames response.headers['X-Frame-Options'] = 'SAMEORIGIN' # In memoriam. http://www.gnuterrypratchett.com/ response.headers['X-Clacks-Overhead'] = 'GNU Terry Pratchett' return response
def update_user_session_timestamp(response): """Mark a user session as accessed at the end of every request.""" if request_has_auth() and current_auth.session: # Setup a callback to update the session after the request has returned a # response to the user-agent. There will be no request or app context in this # callback, so we create a closure containing the necessary data in local vars user_session = current_auth.session ipaddr = request.remote_addr user_agent = str(request.user_agent.string[:250]) @response.call_on_close def mark_session_accessed_after_response(): # App context is needed for the call to statsd in mark_accessed() with app.app_context(): # 1. Add object back to the current database session as it's not # known here. We are NOT using session.merge as we don't need to # refresh data from the db. SQLAlchemy will automatically load # missing data should that be necessary (eg: during login) db.session.add(user_session) # 2. Update user session access timestamp user_session.views.mark_accessed(ipaddr=ipaddr, user_agent=user_agent) # 3. Commit it db.session.commit() return response
def lastuser_cookie(response): """ Save lastuser login cookie and hasuser JS-readable flag cookie. """ if request_has_auth() and hasattr(current_auth, 'cookie'): expires = utcnow() + timedelta(days=365) response.set_cookie( 'lastuser', value=lastuser_oauth.serializer.dumps(current_auth.cookie, header_fields={'v': 1}), max_age=31557600, # Keep this cookie for a year. expires=expires, # Expire one year from now. domain=current_app.config.get( 'LASTUSER_COOKIE_DOMAIN'), # Place cookie in master domain. secure=current_app. config['SESSION_COOKIE_SECURE'], # HTTPS cookie if session is too. httponly=True) # Don't allow reading this from JS. response.set_cookie( 'hasuser', value='1' if current_auth.is_authenticated else '0', max_age=31557600, # Keep this cookie for a year. expires=expires, # Expire one year from now. secure=current_app. config['SESSION_COOKIE_SECURE'], # HTTPS cookie if session is too. httponly=False) # Allow reading this from JS. return response
def lastuser_cookie(response): """ Save lastuser login cookie and hasuser JS-readable flag cookie. """ if request_has_auth() and hasattr(current_auth, 'cookie'): expires = datetime.utcnow() + timedelta(days=365) response.set_cookie('lastuser', value=lastuser_oauth.serializer.dumps(current_auth.cookie, header_fields={'v': 1}), max_age=31557600, # Keep this cookie for a year. expires=expires, # Expire one year from now. domain=current_app.config.get('LASTUSER_COOKIE_DOMAIN'), # Place cookie in master domain. httponly=True) # Don't allow reading this from JS. response.set_cookie('hasuser', value='1' if current_auth.is_authenticated else '0', max_age=31557600, # Keep this cookie for a year. expires=expires, # Expire one year from now. httponly=False) # Allow reading this from JS. return response
def set_lastuser_cookie(response): """Save lastuser login cookie and hasuser JS-readable flag cookie.""" if request_has_auth() and hasattr(current_auth, 'cookie'): expires = utcnow() + timedelta(days=365) response.set_cookie( 'lastuser', value=lastuser_serializer().dumps( current_auth.cookie, header_fields={'v': 1} ), # Keep this cookie for a year. max_age=31557600, # Expire one year from now. expires=expires, # Place cookie in master domain. domain=current_app.config.get('LASTUSER_COOKIE_DOMAIN'), # HTTPS cookie if session is too. secure=current_app.config['SESSION_COOKIE_SECURE'], # Don't allow reading this from JS. httponly=True, # Don't allow lastuser cookie outside first-party use samesite='Strict', ) response.set_cookie( 'hasuser', value='1' if current_auth.is_authenticated else '0', # Keep this cookie for a year. max_age=31557600, # Expire one year from now. expires=expires, # HTTPS cookie if session is too. secure=current_app.config['SESSION_COOKIE_SECURE'], # Allow reading this from JS. httponly=False, # Allow this cookie to be read in third-party website context samesite='Lax', ) return response
def process_response(response): if request.endpoint in ('static', 'baseframe.static'): if 'Access-Control-Allow-Origin' not in response.headers: # This is required for webfont resources # Note: We do not serve static assets in production, nginx does. # That means this piece of code will never be called in production. response.headers['Access-Control-Allow-Origin'] = '*' if 'Vary' in response.headers: vary_values = [ item.strip() for item in response.headers['Vary'].split(',') ] if 'Accept-Language' not in vary_values: vary_values.append('Accept-Language') if 'Cookie' not in vary_values: vary_values.append('Cookie') response.headers['Vary'] = ', '.join(vary_values) else: response.headers['Vary'] = 'Accept-Language, Cookie' # Prevent pages from being placed in an iframe. If the response already # set has a value for this option, let it pass through if 'X-Frame-Options' in response.headers: frameoptions = response.headers.get('X-Frame-Options') if not frameoptions or frameoptions == 'ALLOW': # 'ALLOW' is an unofficial signal from the app to Baseframe. # It signals us to remove the header and not set a default response.headers.pop('X-Frame-Options') else: if request_has_auth() and getattr(current_auth, 'login_required', False): # Protect only login_required pages from appearing in frames response.headers['X-Frame-Options'] = 'SAMEORIGIN' # In memoriam. http://www.gnuterrypratchett.com/ response.headers['X-Clacks-Overhead'] = 'GNU Terry Pratchett' return response
def test_has_current_auth(self): """request_has_auth indicates if current_auth was invoked during a request""" assert not request_has_auth() # Invoke current_auth current_auth.is_anonymous # skipcq: PYL-W0104 assert request_has_auth()
def test_has_current_auth(self): """request_has_auth indicates if current_auth was invoked during a request""" self.assertFalse(request_has_auth()) current_auth.is_anonymous # Invoke current_auth self.assertTrue(request_has_auth())