class Offset(SQLConforming):
    def __init__(self, magnitude: int) -> None:
        assert isinstance(magnitude, int)
        self._magnitude = magnitude
        return

    sql_representation = Immutable(lambda s: s.adapt_integer(s._magnitude))
    magnitude = Immutable(lambda s: s._magnitude)

    @classmethod
    def from_arguments(cls: Type[T],
                       arguments: QueryString,
                       min_value: int = 0) -> T:

        magnitude = arguments.parse_int(key='offset', min_value=min_value)

        return cls(magnitude)

    @classmethod
    def optionally_from_arguments(
            cls: Type[T],
            arguments: QueryString,
            min_value: int = 0,
            fallback_value: Optional[int] = 0) -> Optional[T]:

        magnitude = arguments.optionally_parse_int(key='offset',
                                                   min_value=min_value)

        if magnitude is not None:
            return cls(magnitude)

        if fallback_value is None:
            return None

        return cls(fallback_value)
Beispiel #2
0
class Character:
    def __init__(self, headers: Headers, configuration: Configuration) -> None:

        self._headers = headers
        self._configuration = configuration

        self._ip_address: Optional[IpAddress] = None
        self._user_agent: Optional[UserAgent] = None
        self._language: Optional[AcceptLanguage] = None
        self._accept: Union[int, Optional[ContentType]] = -1
        self._content_type: Union[int, Optional[ContentType]] = -1

        return

    ip_address = Immutable(lambda s: s._parse_ip_address())
    user_agent = Immutable(lambda s: s._parse_user_agent())
    accept_language = Immutable(lambda s: s._parse_accept_language())
    content_type = Immutable(lambda s: s._parse_content_type())
    accept = Immutable(lambda s: s._parse_accept())

    def _parse_ip_address(self) -> IpAddress:

        if self._ip_address is None:
            self._ip_address = IpAddress.from_headers(
                headers=self._headers,
                boundary_ip_header=self._configuration.boundary_ip_header,
                debug=self._configuration.debug,
                debug_address='127.0.0.1')

        return self._ip_address

    def _parse_user_agent(self) -> UserAgent:

        if self._user_agent is None:
            self._user_agent = UserAgent.from_headers(self._headers)

        return self._user_agent

    def _parse_accept_language(self) -> Optional[AcceptLanguage]:

        if self._language is None:
            self._language = AcceptLanguage.from_headers(self._headers)

        return self._language

    def _parse_content_type(self) -> Optional[ContentType]:

        if self._content_type == -1:  # Flagged as unparsed
            self._content_type = ContentType.from_headers(
                self._headers, header='content-type')

        return self._content_type

    def _parse_accept(self) -> Optional[ContentType]:

        if self._accept == -1:  # Flagged as unparsed
            self._accept = ContentType.from_headers(self._headers,
                                                    header='accept')

        return self._accept
class URLParameters:
    """A collection of URL path paraemeters"""
    def __init__(self, targets: List[URLParameter]) -> None:

        assert isinstance(targets, list)
        assert False not in [isinstance(t, URLParameter) for t in targets]

        self._targets = targets
        return

    query_string = Immutable(lambda s: s._form_query_string())
    parameters: List[URLParameter] = Immutable(lambda s: s._targets)

    def _form_query_string(self) -> str:
        if len(self._targets) < 1:
            return ''

        query = '?' + str(self._targets[0])

        for target in self._targets[1:]:
            query += '&' + str(target)

        return query

    def add_to(self, url: str) -> str:
        """Return a URL with parameters bolted on"""
        if len(self._targets) < 1:
            return url

        return url + self.query_string
class Order(SQLConforming):
    def __init__(self, ascending: bool) -> None:

        self._ascending = ascending

        return

    sql_representation = Immutable(lambda s: b'asc'
                                   if s._ascending else b'desc')
    ascending: bool = Immutable(lambda s: s._ascending)
    descending: bool = Immutable(lambda s: not s._ascending)

    def __str__(self) -> str:
        return 'asc' if self._ascending else 'desc'

    @classmethod
    def from_arguments(cls: Type[T],
                       arguments: QueryString,
                       default_to_descending: bool = False) -> T:

        order = arguments.optionally_parse_string(key='order', max_length=32)

        if order is None:
            if default_to_descending is False:
                raise BadRequest(
                    'Supply order=[ascending|descending] parameter')
            return cls(True)

        if order.lower() == 'ascending':
            return cls(True)

        if order.lower() == 'descending':
            return cls(False)

        raise BadRequest('Acceptable `order` values: ascending, descending')
class Credentials:
    """An instance of credentials sent by a client in their HTTP headers"""
    def __init__(self, session_id: str, api_key: str) -> None:

        assert isinstance(session_id, str)
        assert isinstance(api_key, str)

        self._session_id = session_id
        self._api_key = api_key

        return

    api_key = Immutable(lambda s: s._api_key)
    session_id = Immutable(lambda s: s._session_id)

    @classmethod
    def from_headers(cls: Type[T], headers: Headers,
                     configuration: Configuration) -> Optional[T]:
        """Extract credentials from request headers"""

        session_id = headers.value_for(configuration.session_id_name)
        if session_id is None:
            return None

        api_key = headers.value_for(configuration.session_api_key_name)

        if api_key is None:
            return None

        return cls(session_id, api_key)
Beispiel #6
0
class UserAgent(SQLConforming):

    def __init__(
        self,
        body: str
    ) -> None:

        self._body = body

        return

    string = Immutable(lambda s: s._body)
    sql_representation = Immutable(
        lambda s: s.dollar_quote_string(s.string).encode('utf-8')
    )

    @classmethod
    def from_headers(
        cls: Type[T],
        headers: Headers,
        fallback_to: str = 'Unavailable'
    ) -> T:

        body = headers.value_for('User-Agent')
        if body is None:
            return cls(body=fallback_to)

        return cls(body=body)
class Disposition(Codable):
    def __init__(self, sequence: int, count: int, limit: int,
                 offset: int) -> None:

        self._sequence = sequence
        self._count = count
        self._limit = limit
        self._offset = offset

        return

    sequence = Immutable(lambda s: s._sequence)
    count = Immutable(lambda s: s._count)
    limit = Immutable(lambda s: s._limit)
    offset = Immutable(lambda s: s._offset)

    def encode(self) -> Dict[str, int]:
        return {
            'sequence': self._sequence,
            'count': self._count,
            'limit': self._limit,
            'offset': self._offset
        }

    @classmethod
    def decode(cls: Type[T], data: Any) -> T:
        return cls(sequence=data['sequence'],
                   count=data['count'],
                   limit=data['limit'],
                   offset=data['offset'])
Beispiel #8
0
class Datastore:
    """An abstraction of a persistent data storage layer"""

    def __init__(
        self,
        credentials: DatabaseCredentials,
        debug: bool = False
    ) -> None:

        raise NotImplementedError

    connection = Immutable(lambda s: NotImplemented)
    cursor = Immutable(lambda s: NotImplemented)

    def refresh(self) -> None:
        """Connect or re-connect the underlying database connection"""
        raise NotImplementedError

    def execute(
        self,
        query: str,
        arguments: Optional[Dict[str, AnySQLConforming]] = None,
        atomic: bool = False,
        threadsafe: bool = False
    ) -> Optional[Any]:
        """
        Execute a supplied SQL query string with the supplied arguments. The
        Datastore will attempt to recover from broken connections in flight. If
        the query is denoted as atomic. That is, it is not transaction
        dependent.
        """
        raise NotImplementedError

    def close(self) -> None:
        self.cursor.close()
        self.connection.close()
        return

    def commit(self) -> None:
        self.cursor.execute('commit')
        return

    def mogrify(self, query: str, arguments: Optional[Dict[str, Any]]) -> str:
        """Return a string compiled query"""
        return self.cursor.mogrify(query, arguments).decode()

    def rollback(self) -> None:
        """Roll back the current transaction"""
        self.cursor.execute('rollback')

    def start_transaction(self) -> None:
        """Start a database transaction"""
        self.cursor.execute('start transaction')

    @classmethod
    def from_config(cls: Type[T], configuration: Any):
        credentials = configuration.database_credentials
        return cls(credentials, configuration.debug)
Beispiel #9
0
class Order(SQLConforming, Codable):
    def __init__(self, ascending: bool) -> None:

        self._ascending = ascending

        return

    sql_representation = Immutable(lambda s: b'asc'
                                   if s._ascending else b'desc')
    ascending: bool = Immutable(lambda s: s._ascending)
    descending: bool = Immutable(lambda s: not s._ascending)

    def __str__(self) -> str:
        return 'asc' if self._ascending else 'desc'

    def encode(self) -> str:
        if self._ascending is True:
            return 'ascending'
        return 'descending'

    @classmethod
    def decode(cls: Type[T], data: Any) -> T:
        if data == 'asc' or data == 'ascending':
            return cls(ascending=True)
        if data == 'desc' or data == 'descending':
            return cls(ascending=False)
        raise ValueError

    @classmethod
    def optionally_from_request(cls: Type[T],
                                request_data: ParseableData,
                                default_to: Optional[T] = None) -> Optional[T]:

        order = request_data.optionally_parse_string(key='order',
                                                     max_length=32)

        if order is None:
            return default_to

        if order.lower() == 'ascending':
            return cls(True)

        if order.lower() == 'descending':
            return cls(False)

        raise BadRequest('Acceptable `order` values: ascending, descending')

    @classmethod
    def from_request(cls: Type[T],
                     request_data: ParseableData,
                     default_to: Optional[T] = None) -> T:

        order = cls.optionally_from_request(request_data, default_to)

        if order is None:
            raise BadRequest('Supply order=[ascending|descending] parameter')

        return order
class StandaloneAgent(Agent):
    """
    Astract protocol degning an interface for classes who may be granted
    access to read or write objects. At the MVP stage, this is most likely
    to be Humans.
    """
    _Q_RETRIEVE = Query.optionally_from_file(
        'queries/security/agent/retrieve.sql')

    def __init__(self,
                 agent_id: Union[int, str],
                 agent_id_type: Optional[Type] = None) -> None:

        assert isinstance(agent_id, int) or isinstance(agent_id, str)
        if agent_id_type:
            agent_id = agent_id_type(agent_id)
        self._agent_id = agent_id

        return

    agent_id = Immutable(lambda s: s._agent_id)
    indexid = Immutable(lambda s: s._agent_id)

    def __eq__(self, other) -> bool:
        if not isinstance(other.agent_id, int):
            return False
        if not isinstance(self.agent_id, int):
            return False
        return other.agent_id == self.agent_id

    @classmethod
    def retrieve(cls: Type[T], agent_id: int, datastore: Datastore) -> T:
        """Retrieve an Agent with the supplied ID, or raise an exception"""
        agent = cls.optionally_retrieve(agent_id, datastore)
        if agent is None:
            raise NotFound
        return agent

    @classmethod
    def optionally_retrieve(cls: Type[T], public_id: str,
                            datastore: Datastore) -> Optional[T]:
        """Retrieve an Agent with the supplied ID, or None"""
        query = Query.require(cls._Q_RETRIEVE, 'Standalone Agent retrieval')
        result = query.execute(datastore, {'public_id': public_id})
        if result is None:
            return None
        return cls.decode(result)

    @classmethod
    def decode(cls: Type[T], data: Any) -> T:
        """Return an Agent decoded from serialised data"""
        return cls(agent_id=data['agent_id'])
class Fragment(SQLConforming):
    def __init__(self, fragment: str) -> None:
        assert isinstance(fragment, str)
        self._fragment = fragment
        return

    _wildcard_fragment = Immutable(lambda s: '%' + s._fragment + '%')
    sql_representation = Immutable(
        lambda s: s.adapt_string(s._wildcard_fragment))
    value = Immutable(lambda s: s._fragment)

    @classmethod
    def from_arguments(cls: Type[T],
                       arguments: QueryString,
                       max_length: int = 64,
                       min_length: int = 3,
                       key: str = 'fragment') -> T:

        fragment = arguments.parse_string(key=key,
                                          max_length=max_length,
                                          min_length=min_length,
                                          allow_whitespace=True)

        return cls(fragment)

    @classmethod
    def optionally_from_arguments(cls: Type[T],
                                  arguments: QueryString,
                                  max_length: int = 64,
                                  min_length: int = 3,
                                  fallback_to_wildcard: bool = False,
                                  key='fragment') -> Optional[T]:

        fragment = arguments.optionally_parse_string(key=key,
                                                     max_length=max_length,
                                                     min_length=min_length,
                                                     allow_whitespace=True)

        if fragment is None and fallback_to_wildcard is False:
            return None

        if fragment is None and fallback_to_wildcard is True:
            return cls.with_wildcard()

        return cls(fragment)

    @classmethod
    def with_wildcard(cls: Type[T]) -> T:
        return cls('%')
Beispiel #12
0
class AccessControl:
    """
    The headers, methods, and other HTTP Access Control data that define
    access allowed by a browser.
    """
    _ALLOWED_METHODS = 'GET,PUT,POST,OPTIONS,DELETE'
    _ALLOW_CREDENTIALS = 'true'
    _ALLOWED_HEADERS = ['cookie', 'accept', 'content-type']

    def __init__(self, configuration: Configuration) -> None:

        assert isinstance(configuration, Configuration)
        self._configuration = configuration
        return

    _allowed_headers = Immutable(lambda s: ','.join(s._ALLOWED_HEADERS + [
        s._configuration.session_id_name, s._configuration.
        session_cookie_key_name, s._configuration.session_api_key_name
    ]))

    def apply_to_headers(self, headers: Headers) -> Headers:
        """
        Apply access control headerst to the supplied headers and return them.
        """
        headers.add('Access-Control-Allow-Headers', self._allowed_headers)
        headers.add('Access-Control-Allow-Methods', self._ALLOWED_METHODS),
        headers.add('Access-Control-Allow-Credentials',
                    self._ALLOW_CREDENTIALS)
        return headers
Beispiel #13
0
class CORSPolicy:
    """
    Policy on Cross-Origin-Resource-Sharing, aka what origins may make requests
    to the application in the browser.
    """
    _ALL_ORIGINS = '*'
    _HEADER_KEY = 'Access-Control-Allow-Origin'

    def __init__(self, configuration: Configuration) -> None:

        assert isinstance(configuration, Configuration)
        self._configuration = configuration

        return

    allowed_origin = Immutable(lambda s: s._compute_allowed_origin())

    def _compute_allowed_origin(self) -> str:
        """Return the string origin that is allowed to make requests"""
        if self._configuration.disable_cors_restrictions is True:
            return self._ALL_ORIGINS
        if self._configuration.debug is True:
            return self._configuration.local_origin
        return self._configuration.restricted_origin

    def apply_to_headers(self, headers: Headers) -> Headers:
        """Return Headers with CORS policy applied"""
        headers.add(self._HEADER_KEY, self.allowed_origin)
        return headers
class Resource:
    """Abstract class defining machinery for responding to an API request"""
    def __init__(self, datastore: Datastore,
                 configuration: Configuration) -> None:
        assert isinstance(datastore, Datastore)
        self._datastore = datastore
        self._debug = configuration.debug
        self._configuration = configuration
        return

    datastore = Immutable(lambda s: s._datastore)
    debug = Immutable(lambda s: s._debug)
    configuration = Immutable(lambda s: s._configuration)

    def compute_response(self,
                         request_data: Optional[Any],
                         request_arguments: Optional[QueryString],
                         headers: Optional[Headers] = None) -> Encodable:
        """Return serialisable response data"""
        raise NotImplementedError

    def serve(self,
              request_data: Optional[ParseableData],
              request_arguments: Optional[QueryString] = None,
              request_headers: Optional[Headers] = None) -> str:
        """Return a string response body to a request"""
        return self.compute_response(request_data, request_arguments,
                                     request_headers).serialise()

    def assert_read_available_to(
        self, unauthorised_agent: Agent,
        broadcast_candidate: Union[ReadProtected,
                                   List[ReadProtected]]) -> Agent:
        """
        Raise a NotAuthorised error if this Agent may not read the candidate
        """
        if isinstance(broadcast_candidate, list):
            for candidate in broadcast_candidate:
                if not candidate.grants_read_to(unauthorised_agent):
                    raise NotAuthorised
                continue
            return broadcast_candidate

        if not broadcast_candidate.grants_read_to(unauthorised_agent):
            raise NotAuthorised
        return unauthorised_agent
Beispiel #15
0
class _MachineAgent(Agent):
    """An agent representing a Nozomi API itself"""
    def __init__(self) -> None:
        self._agent_id = 1

    def __reduce__(self):
        return (_MachineAgent, ())

    agent_id = Immutable(lambda s: s._agent_id)
Beispiel #16
0
class Agent(SQLConforming, QueryStringConforming):
    """
    Astract protocol defining an interface for classes who may be granted
    access to read or write objects.
    """
    agent_id: Union[str, int] = NotImplemented

    sql_representation = Immutable(lambda s: s.adapt_integer(s.agent_id))
    query_string_value = Immutable(lambda s: str(s.agent_id))

    def __eq__(self, other) -> bool:
        if not isinstance(other.agent_id, int):
            return False
        if not isinstance(self.agent_id, int):
            return False
        if not isinstance(other, Agent):
            return False
        return other.agent_id == self.agent_id
class FileBody:

    def __init__(self, filepath: str) -> None:

        with open(filepath, 'r') as rfile:
            self._body = rfile.read()

        return

    string = Immutable(lambda s: s._body)
Beispiel #18
0
class Headers(AbstractHeaders):
    """
    A set of HTTP headers. Feed Headers a Mapping-conformant data type, for
    example a Dict or a Werkzeug ImmutableMultiDict.
    """
    _TYPE_ERROR = 'The data structure underlying a Headers instance \
unexpectedly contained non-string data for key "{k}", of type {t}. Headers \
must only contain string values. Consider examining the data you fed to the \
Headers initialiser.'

    def __init__(self, raw: Mapping = {}) -> None:
        self._raw = raw
        return

    dictionary = Immutable(lambda s: s._raw)

    def value_for(self, key: str) -> Optional[str]:
        """
        Return the value of a supplied header key, or None if no value
        exists for that key.
        """
        value = self._raw.get(key)
        if value is None:

            for item in self._raw:
                if isinstance(item, tuple) and len(item) > 1:
                    if isinstance(item[0], str):
                        if item[0].lower() == key.lower():
                            return item[1]
                if isinstance(item, str) and item.lower() == key.lower():
                    return self._raw[key.lower()]

            return None

        if not isinstance(value, str):
            raise NozomiError(
                self._TYPE_ERROR.format(k=key, t=str(type(value))),
                HTTPStatusCode.BAD_REQUEST)

        return value

    def getlist(self, key: str) -> List[str]:

        if not hasattr(self._raw, 'getlist'):
            return list()

        return self._raw.getlist(key)

    def add(self, key: str, value: str) -> None:
        if (hasattr(self._raw, 'add')):
            self._raw.add(key, value)
            return
        self._raw[key] = value
        return
Beispiel #19
0
class AbstractSession(Decodable, Agent):

    session_id: str = NotImplemented
    session_key: str = NotImplemented
    agent: Agent = NotImplemented
    perspective: Perspective = NotImplemented
    api_key: str = NotImplemented
    agent_requires_confirmation: bool = NotImplemented
    agent_confirmed: bool = NotImplemented

    agent_id = Immutable(lambda s: s._agent.agent_id)
Beispiel #20
0
class Salt:
    """A cryptographically secure salt for use in passphrase hashing"""

    _SALT_LENGTH = 64

    def __init__(self, salt_b64_string: str) -> None:

        assert isinstance(salt_b64_string, str)
        self._b64_string = salt_b64_string
        return

    string = Immutable(lambda s: str(s))
    utf8_bytes = Immutable(lambda s: str(s).encode('utf-8'))

    def __str__(self) -> str:
        return self._b64_string

    @classmethod
    def create(cls: Type[T]) -> T:
        """Return a new cryptographically secure Salt"""
        return cls(RandomNumber(cls._SALT_LENGTH).base64)
Beispiel #21
0
class Character:
    def __init__(self, headers: Headers, configuration: Configuration) -> None:

        self._headers = headers
        self._configuration = configuration

        self._ip_address: Optional[IpAddress] = None
        self._user_agent: Optional[UserAgent] = None
        self._language: Optional[AcceptLanguage] = None

        return

    ip_address = Immutable(lambda s: s._parse_ip_address())
    user_agent = Immutable(lambda s: s._parse_user_agent())
    accept_language = Immutable(lambda s: s._parse_accept_language())

    def _parse_ip_address(self) -> IpAddress:

        if self._ip_address is None:
            self._ip_address = IpAddress.from_headers(
                headers=self._headers,
                boundary_ip_header=self._configuration.boundary_ip_header,
                debug=self._configuration.debug,
                debug_address='127.0.0.1')

        return self._ip_address

    def _parse_user_agent(self) -> UserAgent:

        if self._user_agent is None:
            self._user_agent = UserAgent.from_headers(self._headers)

        return self._user_agent

    def _parse_accept_language(self) -> Optional[AcceptLanguage]:

        if self._language is None:
            self._language = AcceptLanguage.from_headers(self._headers)

        return self._language
Beispiel #22
0
class Clocked:
    """
    Abstract class providing an interface for classes that wish to measure
    the speed of their initialisation.
    """

    query_start: NozomiTime = NotImplementedError
    query_time: timedelta = Immutable(
        lambda s: s._clocked_format_execution_time())
    query_time_microseconds: int = Immutable(lambda s: int(
        s.query_time.seconds * 1000 + s.query_time.microseconds / 1000))

    _clocked_query_end: Optional[NozomiTime] = None

    @classmethod
    def start_query_clock(self) -> NozomiTime:
        return NozomiTime.utcnow().replace(tzinfo=TimeZoneUTC())

    def mark_initialisation_ended(self,
                                  end_time: Optional[NozomiTime] = None
                                  ) -> None:
        if end_time is None:
            end_time = NozomiTime.utcnow()
        self._clocked_query_end = end_time.replace(tzinfo=TimeZoneUTC())
        return

    def _clocked_format_execution_time(self) -> int:
        query_time = self._clocked_compute_execution_time()
        seconds = query_time.seconds
        microseconds = query_time.microseconds
        return int(((seconds) * 1000 + microseconds) / 1000)

    def _clocked_compute_execution_time(self) -> timedelta:
        if not isinstance(self.query_start, NozomiTime):
            raise NotImplementedError('Implement .query_start')
        if not isinstance(self._clocked_query_end, NozomiTime):
            raise NotImplementedError('Call .mark_initialisation_ended()')
        return self._clocked_query_end - self.query_start
class Format(Decodable, SQLConforming):
    def __init__(self, indexid: int, name: str, header: str) -> None:

        self._indexid = indexid
        self._name = name
        self._header = header

        return

    indexid: int = Immutable(lambda s: s._indexid)
    content_type_header: str = Immutable(lambda s: s._header)

    def __reduce__(self):
        return (Format, (self._indexid, self._name, self._header))

    def encode(self) -> Dict[str, Any]:
        return {
            'indexid': self._indexid,
            'name': self._name,
            'header': self._header
        }

    @classmethod
    def decode(cls: Type[T], data: Any) -> T:
        return Constants.ENUMERATIONS[data['indexid']]

    @classmethod
    def with_id(cls: Type[T], format_id: int) -> Optional[T]:
        if format_id in Constants.ENUMERATIONS:
            return Constants.ENUMERATIONS[format_id]
        return None

    sql_representation = Immutable(lambda s: s.adapt_integer(s.indexid))

    def __eq__(self, other):
        if other._indexid == self._indexid:
            return True
        return False
class IndexSQLConforming(SQLConforming):
    """
    Abstract class defining an interface for classes whose SQL representation
    is their integer datastore indexid.
    """

    indexid: int = NotImplemented

    sql_representation = Immutable(
        lambda s: s._form_index_sql_representation())

    def _form_index_sql_representation(self) -> bytes:
        if not isinstance(self.indexid, int):
            raise NotImplementedError('Implement integer .indexid')
        return self.adapt_integer(self.indexid)
Beispiel #25
0
class URLParameter:
    """A single URL parameter, e.g. beep=boop"""
    def __init__(self, key: str, value: Any) -> None:

        assert isinstance(key, str)
        if isinstance(value, bool):
            value = str(value).lower()
        str(value)  # provoke error early
        self._key = key
        self._value = value

        return

    key: str = Immutable(lambda s: s._key)

    def __str__(self) -> str:
        return self._key + '=' + str(self._value)
Beispiel #26
0
class Configuration:
    """
    Abstract class defining an interface for a concrete class providing
    configuration to a Nozomi application
    """

    debug: bool = NotImplemented
    api_endpoint: str = NotImplemented
    public_api_endpoint: str = Immutable(
        lambda s: s.api_endpoint
    )

    # Sessions

    session_seconds_to_live: int = NotImplemented
    session_api_key_name: str = NotImplemented
    session_cookie_key_name: str = NotImplemented
    session_id_name: str = NotImplemented
    session_flag_cookie_name: str = NotImplemented

    internal_psk_header: str = NotImplemented
    internal_psk: Optional[InternalKey] = NotImplemented

    forwarded_agent_header: str = NotImplemented

    standard_css_styles: List[Union[str, Style]] = NotImplemented
    standard_js_classes: List[Union[str, JavaScriptClass]] = NotImplemented
    standard_js_scripts: List[Union[str, Script]] = NotImplemented

    database_credentials: DatabaseCredentials = NotImplemented

    api_agent: Agent = NotImplemented

    # CORS
    local_origin: str = NotImplemented
    restricted_origin: str = NotImplemented
    disable_cors_restriction: bool = NotImplemented
    development_origins: Optional[List[str]] = None

    # Other
    server_name: str = NotImplemented
    boundary_ip_header: str = NotImplemented

    # Template Rendering
    template_directory = 'templates'
Beispiel #27
0
class URLParameter:
    """A single URL parameter, e.g. beep=boop"""
    def __init__(self, key: str, value: Any) -> None:

        if not isinstance(key, str):
            raise TypeError('key must be of type `str`')

        if isinstance(value, bool):
            value = str(value).lower()

        if isinstance(value, Enum):
            value = value.value

        if isinstance(value, QueryStringConforming):
            value = value.query_string_value

        try:
            str(value)
        except:
            raise TypeError(
                'value must be of type that may be coerced to `str')

        self._key = key
        self._value = value

        return

    @staticmethod
    def remove_targets_with(key: str, parameters: List[Self]) -> List[Self]:

        retained_targets: List[URLParameter] = []
        for target in parameters:
            if target._key == key:
                continue
            retained_targets.append(target)
            continue

        return retained_targets

    key: str = Immutable(lambda s: s._key)

    def __str__(self) -> str:
        return self._key + '=' + str(self._value)
class Perspective:
    """
    Abstract class defining a protocol for concrete classes that identify
    perspectives
    """
    def __init__(self, perspective_id: int) -> None:

        self._perpective_id = perspective_id

        return

    perspective_id = Immutable(lambda s: s._perpective_id)

    def __eq__(self, other) -> bool:
        if not isinstance(other, Perspective):
            return False
        if not self.perspective_id == other.perspective_id:
            return False
        return True
Beispiel #29
0
class Cookies:
    """An instance of a set of cookies sent by a client"""
    def __init__(self, raw_cookies: str) -> None:

        self._cookies: Dict[str, str] = dict()
        self._raw_cookies = raw_cookies

        if raw_cookies is None:
            return

        for cookie in raw_cookies.split(';'):
            pieces = cookie.strip().split('=')
            try:
                self._cookies[pieces[0]] = pieces[1]
            except IndexError:
                raise RuntimeError('Invalid cookies supplied')

        return

    is_empty: bool = Immutable(lambda s: len(s._cookies) < 1)

    def contains(self, cookie_name: str) -> bool:
        """Return true if the jar contains the specified cookie"""
        if cookie_name in self._cookies.keys():
            return True
        return False

    def value_for(self, cookie_name: str) -> str:
        """
        Return the value of a specified cookie
        """
        if self.contains(cookie_name) is False:
            raise ValueError('Cookie with specified name does not exist')
        return self._cookies[cookie_name]

    @classmethod
    def from_headers(cls: Type[T], headers: Headers) -> Optional[T]:
        """Return Cookies parsed from request headers"""
        raw_cookies = headers.value_for('Cookie')
        if raw_cookies is None:
            return None
        return cls(raw_cookies)
Beispiel #30
0
class AbstractHeaders:
    """
    A set of HTTP headers. Feed Headers a Mapping-conformant data type, for
    example a Dict or a Werkzeug ImmutableMultiDict.
    """
    def __init__(self, raw: Mapping = {}) -> None:
        self._raw = raw
        return

    dictionary = Immutable(lambda s: s._raw)

    def value_for(self, key: str) -> Optional[str]:
        """
        Return the value of a supplied header key, or None if no value
        exists for that key.
        """
        raise NotImplementedError

    def getlist(self, key: str) -> List[str]:
        raise NotImplementedError

    def add(self, key: str, value: str) -> None:
        raise NotImplementedError