Viewing file: obj.py (8.92 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
"""Module contains classes used by the Apache Configurator.""" import re
from acme.magic_typing import Set # pylint: disable=unused-import, no-name-in-module from certbot.plugins import common
class Addr(common.Addr): """Represents an Apache address."""
def __eq__(self, other): """This is defined as equivalent within Apache.
ip_addr:* == ip_addr
""" if isinstance(other, self.__class__): return ((self.tup == other.tup) or (self.tup[0] == other.tup[0] and self.is_wildcard() and other.is_wildcard())) return False
def __ne__(self, other): return not self.__eq__(other)
def __repr__(self): return "certbot_apache.obj.Addr(" + repr(self.tup) + ")"
def __hash__(self): # Python 3 requires explicit overridden for __hash__ if __eq__ or # __cmp__ is overridden. See https://bugs.python.org/issue2235 return super(Addr, self).__hash__()
def _addr_less_specific(self, addr): """Returns if addr.get_addr() is more specific than self.get_addr().""" # pylint: disable=protected-access return addr._rank_specific_addr() > self._rank_specific_addr()
def _rank_specific_addr(self): """Returns numerical rank for get_addr()
:returns: 2 - FQ, 1 - wildcard, 0 - _default_ :rtype: int
""" if self.get_addr() == "_default_": return 0 elif self.get_addr() == "*": return 1 else: return 2
def conflicts(self, addr): r"""Returns if address could conflict with correct function of self.
Could addr take away service provided by self within Apache?
.. note::IP Address is more important than wildcard. Connection from 127.0.0.1:80 with choices of *:80 and 127.0.0.1:* chooses 127.0.0.1:\*
.. todo:: Handle domain name addrs...
Examples:
========================================= ===== ``127.0.0.1:\*.conflicts(127.0.0.1:443)`` True ``127.0.0.1:443.conflicts(127.0.0.1:\*)`` False ``\*:443.conflicts(\*:80)`` False ``_default_:443.conflicts(\*:443)`` True ========================================= =====
""" if self._addr_less_specific(addr): return True elif self.get_addr() == addr.get_addr(): if self.is_wildcard() or self.get_port() == addr.get_port(): return True return False
def is_wildcard(self): """Returns if address has a wildcard port.""" return self.tup[1] == "*" or not self.tup[1]
def get_sni_addr(self, port): """Returns the least specific address that resolves on the port.
Examples:
- ``1.2.3.4:443`` -> ``1.2.3.4:<port>`` - ``1.2.3.4:*`` -> ``1.2.3.4:*``
:param str port: Desired port
""" if self.is_wildcard(): return self
return self.get_addr_obj(port)
class VirtualHost(object): # pylint: disable=too-few-public-methods """Represents an Apache Virtualhost.
:ivar str filep: file path of VH :ivar str path: Augeas path to virtual host :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`common.Addr`) :ivar str name: ServerName of VHost :ivar list aliases: Server aliases of vhost (:class:`list` of :class:`str`)
:ivar bool ssl: SSLEngine on in vhost :ivar bool enabled: Virtual host is enabled :ivar bool modmacro: VirtualHost is using mod_macro :ivar VirtualHost ancestor: A non-SSL VirtualHost this is based on
https://httpd.apache.org/docs/2.4/vhosts/details.html
.. todo:: Any vhost that includes the magic _default_ wildcard is given the same ServerName as the main server.
""" # ?: is used for not returning enclosed characters strip_name = re.compile(r"^(?:.+://)?([^ :$]*)")
def __init__(self, filep, path, addrs, ssl, enabled, name=None, aliases=None, modmacro=False, ancestor=None):
# pylint: disable=too-many-arguments """Initialize a VH.""" self.filep = filep self.path = path self.addrs = addrs self.name = name self.aliases = aliases if aliases is not None else set() self.ssl = ssl self.enabled = enabled self.modmacro = modmacro self.ancestor = ancestor
def get_names(self): """Return a set of all names.""" all_names = set() # type: Set[str] all_names.update(self.aliases) # Strip out any scheme:// and <port> field from servername if self.name is not None: all_names.add(VirtualHost.strip_name.findall(self.name)[0])
return all_names
def __str__(self): return ( "File: {filename}\n" "Vhost path: {vhpath}\n" "Addresses: {addrs}\n" "Name: {name}\n" "Aliases: {aliases}\n" "TLS Enabled: {tls}\n" "Site Enabled: {active}\n" "mod_macro Vhost: {modmacro}".format( filename=self.filep, vhpath=self.path, addrs=", ".join(str(addr) for addr in self.addrs), name=self.name if self.name is not None else "", aliases=", ".join(name for name in self.aliases), tls="Yes" if self.ssl else "No", active="Yes" if self.enabled else "No", modmacro="Yes" if self.modmacro else "No"))
def display_repr(self): """Return a representation of VHost to be used in dialog""" return ( "File: {filename}\n" "Addresses: {addrs}\n" "Names: {names}\n" "HTTPS: {https}\n".format( filename=self.filep, addrs=", ".join(str(addr) for addr in self.addrs), names=", ".join(self.get_names()), https="Yes" if self.ssl else "No"))
def __eq__(self, other): if isinstance(other, self.__class__): return (self.filep == other.filep and self.path == other.path and self.addrs == other.addrs and self.get_names() == other.get_names() and self.ssl == other.ssl and self.enabled == other.enabled and self.modmacro == other.modmacro)
return False
def __ne__(self, other): return not self.__eq__(other)
def __hash__(self): return hash((self.filep, self.path, tuple(self.addrs), tuple(self.get_names()), self.ssl, self.enabled, self.modmacro))
def conflicts(self, addrs): """See if vhost conflicts with any of the addrs.
This determines whether or not these addresses would/could overwrite the vhost addresses.
:param addrs: Iterable Addresses :type addrs: Iterable :class:~obj.Addr
:returns: If addresses conflicts with vhost :rtype: bool
""" for pot_addr in addrs: for addr in self.addrs: if addr.conflicts(pot_addr): return True return False
def same_server(self, vhost, generic=False): """Determines if the vhost is the same 'server'.
Used in redirection - indicates whether or not the two virtual hosts serve on the exact same IP combinations, but different ports. The generic flag indicates that that we're trying to match to a default or generic vhost
.. todo:: Handle _default_
"""
if not generic: if vhost.get_names() != self.get_names(): return False
# If equal and set is not empty... assume same server if self.name is not None or self.aliases: return True # If we're looking for a generic vhost, # don't return one with a ServerName elif self.name: return False
# Both sets of names are empty.
# Make conservative educated guess... this is very restrictive # Consider adding more safety checks. if len(vhost.addrs) != len(self.addrs): return False
# already_found acts to keep everything very conservative. # Don't allow multiple ip:ports in same set. already_found = set() # type: Set[str]
for addr in vhost.addrs: for local_addr in self.addrs: if (local_addr.get_addr() == addr.get_addr() and local_addr != addr and local_addr.get_addr() not in already_found):
# This intends to make sure we aren't double counting... # e.g. 127.0.0.1:* - We require same number of addrs # currently already_found.add(local_addr.get_addr()) break else: return False
return True
|