#!/usr/bin/env python3
"""PPPA Proxy Service.

Handles incoming requests for access to the private PPA containing
Landscape On Premises packages, authenticates the account for a
valid entitlement and proxies the request to the actual PPPA if the
access is granted.
"""

import os
import re
import glob

from OpenSSL.crypto import load_certificate, FILETYPE_PEM

from twisted.application.internet import TCPServer
from twisted.application.service import Application
from twisted.cred.portal import Portal
from twisted.web.guard import BasicCredentialFactory, HTTPAuthSessionWrapper
from twisted.web.server import Site

from canonical.twisted.http import combinedLogFormatterWithUser

from canonical.landscape.scripts.pppa_proxy import run
from canonical.landscape.setup import load_config
from canonical.landscape.pppa import (
    ArchiveRealm, ArchiveChecker, ARCHIVE_REALM_NAME)


if __name__ == "__main__":
    run()


config = load_config("pppa-proxy")
proxy_port = int(config.get("pppa-proxy", "base-port"))

certificateAuthorityMap = {}

for certFileName in glob.glob("/etc/ssl/certs/*.pem"):
    # There might be some dead symlinks in there, so let's make sure it's real.
    if os.path.exists(certFileName):
        data = open(certFileName).read()
        x509 = load_certificate(FILETYPE_PEM, data)
        digest = x509.digest('sha1')
        # Now, de-duplicate in case the same cert has multiple names.
        certificateAuthorityMap[digest] = x509


def verifyHostname(connection, x509, errno, depth, preverifyOK):
    # Only check depth == 0 on chained certificates.
    host = "private-ppa.launchpad.net"
    if depth == 0:
        commonName = x509.get_subject().commonName
        if commonName is None:
            return False
        # The commonName might contain a wildcard, so turn it into a
        # regex for checking.
        commonName_re = "^%s$" % commonName.replace(
            ".", "\.").replace("*", "[^.]*")
        if not re.search(commonName_re, host, re.I):
            return False
    return preverifyOK


realm = ArchiveRealm(config.get("pppa-proxy"),
                     certificateAuthorityMap.values(),
                     verifyHostname)
portal = Portal(realm, [ArchiveChecker()])

resource = HTTPAuthSessionWrapper(
    portal, [BasicCredentialFactory(ARCHIVE_REALM_NAME)])

application = Application("pppa-proxy")
site = Site(resource, logFormatter=combinedLogFormatterWithUser)
service = TCPServer(proxy_port, site)
service.setServiceParent(application)
