# Copyright 2020 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """Request-scoped context.""" from tensorboard import auth as auth_lib # A `RequestContext` value is stored on WSGI environments under this key. _WSGI_KEY = "tensorboard.request_context" class RequestContext: """Container of request-scoped values. This context is for cross-cutting concerns: authentication, authorization, auditing, internationalization, logging, and so on. It is not simply for passing commonly used parameters to functions. `RequestContext` values are to be treated as immutable. Fields: auth: An `AuthContext`, which may be empty but is never `None`. remote_ip: An `ipaddress.IPv4Address` or `ipaddress.IPv6Address` or None. Best guess of the IP Address of the end user. x_forwarded_for: A tuple of `ipaddress.IPv4Address` or `ipaddress.IPv6Address`, which may be empty but is never None. This should be parsed value of X-Forwarded-For HTTP header from the request. client_feature_flags: A dict of string to arbitrary type. These represent feature flag key/value pairs sent by the client application. Usage of client_feature_flags should know the name of the feature flag key and should know and validate the type of the value. """ def __init__( self, auth=None, remote_ip=None, x_forwarded_for=None, client_feature_flags=None, ): """Create a request context. The argument list is sorted and may be extended in the future; therefore, callers must pass only named arguments to this initializer. Args: See "Fields" on class docstring. All arguments are optional and will be replaced with default values if appropriate. """ self._auth = auth if auth is not None else auth_lib.AuthContext.empty() self._remote_ip = remote_ip self._x_forwarded_for = x_forwarded_for or () self._client_feature_flags = client_feature_flags or {} @property def auth(self): return self._auth @property def remote_ip(self): return self._remote_ip @property def x_forwarded_for(self): return self._x_forwarded_for @property def client_feature_flags(self): return self._client_feature_flags def replace(self, **kwargs): """Create a copy of this context with updated key-value pairs. Analogous to `namedtuple._replace`. For example, to create a new request context like `ctx` but with auth context `auth`, call `ctx.replace(auth=auth)`. Args: As to `__init__`. Returns: A new context like this one but with the specified updates. """ kwargs.setdefault("auth", self.auth) kwargs.setdefault("remote_ip", self.remote_ip) kwargs.setdefault("x_forwarded_for", self.x_forwarded_for) kwargs.setdefault("client_feature_flags", self.client_feature_flags) return type(self)(**kwargs) def from_environ(environ): """Get a `RequestContext` from a WSGI environment. See also `set_in_environ`. Args: environ: A WSGI environment (see PEP 3333). Returns: The `RequestContext` stored in the WSGI environment, or an empty `RequestContext` if none is stored. """ result = environ.get(_WSGI_KEY) return result if result is not None else RequestContext() def set_in_environ(environ, ctx): """Set the `RequestContext` in a WSGI environment. After `set_in_environ(e, ctx)`, `from_environ(e) is ctx`. The input environment is mutated. Args: environ: A WSGI environment to update. ctx: A new `RequestContext` value. """ environ[_WSGI_KEY] = ctx