.. module:: flashpolicies.policies


Utilities for generating cross-domain policy files
==================================================

Internally, all policy files generated by django-flashpolicies are
represented by instances of :class:`flashpolicies.policies.Policy`,
which understands how to handle the various permitted options in
policy files and can generate the correct XML. This documentation
covers :class:`Policy` objects and their API, but is not and should
not be taken to be documentation on the format and options for
cross-domain policy files; `Adobe's cross-domain policy specification
<http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html>`_
is the canonical source for that information.


Interaction with :class:`Policy` objects
----------------------------------------

For most cases, instantiating a :class:`Policy` object with one or
more domains will accomplish the desired effect. The property
:attr:`~Policy.xml_dom` will yield a
:class:`~xml.dom.minidom.Document` object representing the policy's
XML; for information on working with these objects, consult the
documentation for `the xml.dom.minidom module in the Python standard
library <http://docs.python.org/library/xml.dom.minidom.html>`_.


Serializing :class:`Policy` objects
-----------------------------------

There are two similar but different ways to serialize the underlying
XML. One is to use :func:`str` on a :class:`Policy` instance, like so:

.. code-block:: pycon

   >>> from flashpolicies import policies
   >>> my_policy = policies.Policy('media.example.com', 'api.example.com')
   >>> print(str(my_policy))
   <?xml version="1.0" ?>
   <!DOCTYPE cross-domain-policy
     SYSTEM 'http://www.adobe.com/xml/dtds/cross-domain-policy.dtd'>
   <cross-domain-policy>
           <allow-access-from domain="media.example.com"/>
           <allow-access-from domain="api.example.com"/>
   </cross-domain-policy>

The other is to call the :meth:`~Policy.serialize` method. The
difference between these options is:

1. :func:`str` will, as is required by Python's semantics, produce a
   result of type :class:`str`, which is a Unicode string; this means
   the output is not in any particular encoding, and will omit the
   `encoding` declaration of the XML prolog.

2. :meth:`~Policy.serialize` will, on the other hand, always return a
   UTF-8-encoded :class:`bytes` object, so the output of
   :meth:`~Policy.serialize` *will* include an `encoding` declaration
   in its XML prolog.

In general, :func:`str` should be used to inspect a :class:`Policy`
for debugging or educational purposes, while :meth:`~Policy.serialize`
should be used any time the output will actually be treated as a
policy file (i.e., if writing your own policy-serving view, or if
serializing the policy to a file). The built-in
:func:`~flashpolicies.views.serve` view uses
:meth:`~Policy.serialize`.


API reference
-------------

.. class:: Policy

   Wrapper object for creating and manipulating a Flash cross-domain
   policy.

   In the most common case -- specifying one or more domains from
   which to allow access -- pass the domains when initializing. For
   example:

   .. code-block:: python

      my_policy = Policy('media.example.com', 'api.example.com')

   .. attribute:: xml_dom

      A read-only property which returns an XML representation of this
      policy, as an :class:`xml.dom.minidom.Document` object.

   .. method:: serialize()

      Serialize this policy to UTF-8-encoded bytes suitable for
      serving over HTTP or writing to a file.

      :rtype: :class:`bytes`

   .. method:: allow_domain(domain, to_ports=None, secure=True)

      Allows access for Flash content served from a particular domain.

      :param domain: The domain from which to allow access. May be
         either a full domain name (e.g., `"example.com"`) or a
         wildcard (e.g., `"example.com"`). Due to serious potential
         security concerns, it is strongly recommended that you avoid
         wildcard domain values.
      :type domain: str
      :param to_ports: (only for socket policy files) The ports (as
         :class:`str`) the domain will be permitted to access. Each
         port may be either a port number (e.g., `"80"`), a range of
         ports (e.g., `"80-120"`) or the wildcard value `"*"`, which
         will permit all ports.
      :type to_ports: typing.Iterable
      :param bool secure: If :data:`True`, will require the security
         level of the HTTP protocol for Flash content to match that of
         this policy file; for example, if the policy file was
         retrieved via HTTPS, Flash content from `domain` must also be
         retrieved via HTTPS. If :data:`False`, this matching of
         security levels will be disabled. It is strongly recommended
         that you not disable the matching of security levels.
      :rtype: :data:`None`
      :raises TypeError: if the current metapolicy is
         :data:`SITE_CONTROL_NONE`. See :meth:`metapolicy` for
         details.

   .. method:: allow_headers(domain, headers, secure=True)

      Allows Flash content from a particular domain to push data via
      HTTP headers.

      :param str domain: The domain from which to allow access. May be
         either a full domain name (e.g., `"example.com"`) or a
         wildcard (e.g., `"*.example.com"`). Due to serious potential
         security concerns, it is strongly recommended that you avoid
         wildcard domain values.
      :param headers: The HTTP header names (as :class:`str`) in which
         data may be submitted.
      :type headers: typing.Iterable
      :param bool secure: If :data:`True`, will require the security
         level of the HTTP protocol for Flash content to match that of
         this policy file; for example, if the policy file was
         retrieved via HTTPS, Flash content from `domain` must also be
         retrieved via HTTPS. If :data:`False`, this matching of
         security levels will be disabled. It is strongly recommended
         that you not disable the matching of security levels.
      :rtype: :data:`None`
      :raises TypeError: if the current metapolicy is
         :data:`SITE_CONTROL_NONE`. See :meth:`metapolicy` for
         details.

   .. method:: allow_identity(fingerprint)

      Allows access from digitally-signed documents.

      The XML resulting from use of this method will include both the
      key fingerprint and the name of an algorithm used to calculate
      the fingerprint. At the moment, `"sha-1"` is the only value
      defined in the cross-domain policy specification for the
      `fingerprint-algorithm` attribute of the `certificate`
      element (which is the element produced by this method), and so
      an argument for this is omitted; if additional algorithms are
      added to the specification, support will be added in a
      backwards-compatible fashion (likely through an argument
      defaulting to SHA-1).

      :param str fingerprint: The fingerprint of the signing key to
         allow.
      :rtype: :data:`None`
      :raises TypeError: if the current metapolicy is
         :data:`SITE_CONTROL_NONE`. See :meth:`metapolicy` for
         details.

   .. method:: metapolicy(permitted)

      Sets metapolicy information (only applicable to master policy
      files), determining which other policy files may be used on the
      same domain.

      By default, Flash assumes a default metapolicy of
      `"master-only"` (except for socket policies, which assume a
      default of `"all"`), so if this is the desired metapolicy
      (and, for security reasons, it often is), this method does not
      need to be called.

      Note that a metapolicy of `"none"` forbids **all** access, even
      if one or more domains, headers or identities have previously
      been specified as allowed. As such, setting the metapolicy to
      `"none"` will remove all access previously granted by
      :meth:`allow_domain`, :meth:`allow_headers` or
      :meth:`allow_identity`. Additionally, attempting to grant access
      via :meth:`allow_domain`, :meth:`allow_headers` or
      :meth:`allow_identity` will, when the metapolicy is `"none"`,
      raise :exc:`TypeError`.
      
      :param str permitted: The metapolicy to use. Acceptable values
         are those listed in the cross-domain policy specification,
         and are also available as :ref:`a set of constants defined in
         this module <metapolicy-constants>`.
      :rtype: :data:`None`
      :raises TypeError: when `permitted` is not one of the
         accceptable metapolicy values from the Adobe cross-domain
         policy specification.


.. _metapolicy-constants:

Available constants
-------------------

For ease of working with metapolicies, the following constants are
defined, and correspond to `the acceptable values for metapolicies as
defined in the cross-domain policy specification
<http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html#site-control>`_.

.. data:: SITE_CONTROL_ALL

   All policy files available on the current domain are
   permitted. Actual value is the string `"all"`.

.. data:: SITE_CONTROL_BY_CONTENT_TYPE

   Only policy files served from the current domain with an HTTP
   `Content-Type` of "text/x-cross-domain-policy" are
   permitted. Actual value is the string `"by-content-type"`.

.. data:: SITE_CONTROL_BY_FTP_FILENAME

   Only policy files served from the current domain as files named
   `crossdomain.xml` are permitted. Actual value is the string
   `"by-ftp-filename"`.

.. data:: SITE_CONTROL_MASTER_ONLY

   Only the master policy file for this domain -- the policy served
   from the URL `/crossdomain.xml` -- is permitted. Actual value is
   the string `"master-only"`.

.. data:: SITE_CONTROL_NONE

   No policy files are permitted, including the master policy
   file. Actual value is the string `"none"`.

.. data:: VALID_SITE_CONTROL

   A tuple containing the above constants, for convenient validation
   of metapolicy values.
