Source

hgssoauthentication / hgssoauthentication.py

# Copyright 2012 Dominik Ruf <dominikruf@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#


import mercurial.url

import urllib2
import httplib
import os
from base64 import encodestring, decodestring


try:
    from sspi import ClientAuth
except ImportError, e:
    try:
        import kerberos
    except ImportError, e:
        raise ImportError('Neigther sspi nor kerberos module is available')

class SSPIAuthHandler(urllib2.BaseHandler):
    """auth handler for urllib2 that does Kerberos/NTLM/SSPI HTTP Negotiate Authentication
    """
    
    handler_order = 480 # TODO: test this by enabling basic auth 
    
    def __init__(self, ui, passmgr):
        pass
    
    def http_error_401(self, req, fp, code, msg, headers):
        supported_schemes = [s.strip() for s in headers.get("WWW-Authenticate", "").split(",")]
        
        if('NTLM' in supported_schemes):
            # 1. request
            ca = ClientAuth("NTLM", auth_info=None)
            auth_scheme = ca.pkg_info['Name']
            out_buf = ca.authorize(None)[1]
            data = out_buf[0].Buffer
            auth = encodestring(data).replace("\012", "")
            # since urllib2 doesn't support keepalive we create our own new connection
            h = httplib.HTTPConnection(req.host)
            h.putrequest('GET', req._Request__r_host)
            h.putheader('Authorization', auth_scheme + ' ' + auth)
            h.putheader('Content-Length', '0')
            h.endheaders()
            resp = h.getresponse()
            
            # 2.request
            schemes = [s.strip() for s in resp.msg.get("WWW-Authenticate", "").split(",")]
            for scheme in schemes:
                if scheme.startswith(auth_scheme):
                    data = decodestring(scheme[len(auth_scheme)+1:])
                    break
            resp.read()
            
            out_buf = ca.authorize(data)[1]
            data = out_buf[0].Buffer
            
            auth = encodestring(data).replace("\012", "")
            h.putrequest('GET', req._Request__r_host)
            h.putheader('Authorization', auth_scheme + ' ' + auth)
            h.putheader('Content-Length', '0')
            for e in req.unredirected_hdrs.items() + req.headers.items():
                if(e[0] != 'Host'):
                    h.putheader(e[0], e[1])
            h.endheaders()
            resp = h.getresponse()
            
            fp.headers = resp.msg
            fp.read = resp.read
            return fp
        
        elif('Negotiate' in supported_schemes):
            ca = ClientAuth("Kerberos", targetspn='HTTP/%s@%s' % (req.host, os.environ['USERDNSDOMAIN']), auth_info=None)
            out_buf = ca.authorize(None)[1]
            data = out_buf[0].Buffer
            auth = encodestring(data).replace("\012", "")
            req.add_header('Authorization', 'Negotiate' + ' ' + auth)
            return self.parent.open(req)

class KerberosAuthHandler(urllib2.BaseHandler):
    """auth handler for urllib2 that does Kerberos HTTP Negotiate Authentication
    """

    handler_order = 480

    def __init__(self, ui, passmgr):
        pass

    def http_error_401(self, req, fp, code, msg, headers):
        supported_schemes = [s.strip() for s in headers.get("WWW-Authenticate", "").split(",")]

        context = kerberos.authGSSClientInit("HTTP@%s" % req.get_host())[1]
        kerberos.authGSSClientStep(context, supported_schemes[0])
        response = kerberos.authGSSClientResponse(context)
        req.add_unredirected_header('Authorization', "Negotiate %s" % response)
        resp = self.parent.open(req)
        # make sure the response came from the correct server
        server_token = resp.info().get("WWW-Authenticate", "").split(",")[0].split()[1]
        server_auth_result = kerberos.authGSSClientStep(context, server_token)
        if(server_auth_result < 1):
            raise Exception("Server authentication error: %s" % str(server_auth_result))
        return resp
            
def uisetup(ui):
    if('ClientAuth' in globals()):
        mercurial.url.handlerfuncs.append(SSPIAuthHandler)
    elif('kerberos' in globals()):
        mercurial.url.handlerfuncs.append(KerberosAuthHandler)
    
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.