File size: 4,441 Bytes
cf2a15a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# 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