http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/phoenixdb/types.py
----------------------------------------------------------------------
diff --git a/python/phoenixdb/types.py b/python/phoenixdb/types.py
deleted file mode 100644
index f41355a..0000000
--- a/python/phoenixdb/types.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import sys
-import time
-import datetime
-from decimal import Decimal
-from phoenixdb.avatica.proto import common_pb2
-
-__all__ = [
-    'Date', 'Time', 'Timestamp', 'DateFromTicks', 'TimeFromTicks', 
'TimestampFromTicks',
-    'Binary', 'STRING', 'BINARY', 'NUMBER', 'DATETIME', 'ROWID', 'BOOLEAN',
-    'JAVA_CLASSES', 'JAVA_CLASSES_MAP', 'TypeHelper',
-]
-
-
-def Date(year, month, day):
-    """Constructs an object holding a date value."""
-    return datetime.date(year, month, day)
-
-
-def Time(hour, minute, second):
-    """Constructs an object holding a time value."""
-    return datetime.time(hour, minute, second)
-
-
-def Timestamp(year, month, day, hour, minute, second):
-    """Constructs an object holding a datetime/timestamp value."""
-    return datetime.datetime(year, month, day, hour, minute, second)
-
-
-def DateFromTicks(ticks):
-    """Constructs an object holding a date value from the given UNIX 
timestamp."""
-    return Date(*time.localtime(ticks)[:3])
-
-
-def TimeFromTicks(ticks):
-    """Constructs an object holding a time value from the given UNIX 
timestamp."""
-    return Time(*time.localtime(ticks)[3:6])
-
-
-def TimestampFromTicks(ticks):
-    """Constructs an object holding a datetime/timestamp value from the given 
UNIX timestamp."""
-    return Timestamp(*time.localtime(ticks)[:6])
-
-
-def Binary(value):
-    """Constructs an object capable of holding a binary (long) string value."""
-    return bytes(value)
-
-
-def time_from_java_sql_time(n):
-    dt = datetime.datetime(1970, 1, 1) + datetime.timedelta(milliseconds=n)
-    return dt.time()
-
-
-def time_to_java_sql_time(t):
-    return ((t.hour * 60 + t.minute) * 60 + t.second) * 1000 + t.microsecond 
// 1000
-
-
-def date_from_java_sql_date(n):
-    return datetime.date(1970, 1, 1) + datetime.timedelta(days=n)
-
-
-def date_to_java_sql_date(d):
-    if isinstance(d, datetime.datetime):
-        d = d.date()
-    td = d - datetime.date(1970, 1, 1)
-    return td.days
-
-
-def datetime_from_java_sql_timestamp(n):
-    return datetime.datetime(1970, 1, 1) + datetime.timedelta(milliseconds=n)
-
-
-def datetime_to_java_sql_timestamp(d):
-    td = d - datetime.datetime(1970, 1, 1)
-    return td.microseconds // 1000 + (td.seconds + td.days * 24 * 3600) * 1000
-
-
-class ColumnType(object):
-
-    def __init__(self, eq_types):
-        self.eq_types = tuple(eq_types)
-        self.eq_types_set = set(eq_types)
-
-    def __eq__(self, other):
-        return other in self.eq_types_set
-
-    def __cmp__(self, other):
-        if other in self.eq_types_set:
-            return 0
-        if other < self.eq_types:
-            return 1
-        else:
-            return -1
-
-
-STRING = ColumnType(['VARCHAR', 'CHAR'])
-"""Type object that can be used to describe string-based columns."""
-
-BINARY = ColumnType(['BINARY', 'VARBINARY'])
-"""Type object that can be used to describe (long) binary columns."""
-
-NUMBER = ColumnType([
-    'INTEGER', 'UNSIGNED_INT', 'BIGINT', 'UNSIGNED_LONG', 'TINYINT', 
'UNSIGNED_TINYINT',
-    'SMALLINT', 'UNSIGNED_SMALLINT', 'FLOAT', 'UNSIGNED_FLOAT', 'DOUBLE', 
'UNSIGNED_DOUBLE', 'DECIMAL'
-])
-"""Type object that can be used to describe numeric columns."""
-
-DATETIME = ColumnType(['TIME', 'DATE', 'TIMESTAMP', 'UNSIGNED_TIME', 
'UNSIGNED_DATE', 'UNSIGNED_TIMESTAMP'])
-"""Type object that can be used to describe date/time columns."""
-
-ROWID = ColumnType([])
-"""Only implemented for DB API 2.0 compatibility, not used."""
-
-BOOLEAN = ColumnType(['BOOLEAN'])
-"""Type object that can be used to describe boolean columns. This is a 
phoenixdb-specific extension."""
-
-
-# XXX ARRAY
-
-if sys.version_info[0] < 3:
-    _long = long  # noqa: F821
-else:
-    _long = int
-
-JAVA_CLASSES = {
-    'bool_value': [
-        ('java.lang.Boolean', common_pb2.BOOLEAN, None, None),
-    ],
-    'string_value': [
-        ('java.lang.Character', common_pb2.CHARACTER, None, None),
-        ('java.lang.String', common_pb2.STRING, None, None),
-        ('java.math.BigDecimal', common_pb2.BIG_DECIMAL, str, Decimal),
-    ],
-    'number_value': [
-        ('java.lang.Integer', common_pb2.INTEGER, None, int),
-        ('java.lang.Short', common_pb2.SHORT, None, int),
-        ('java.lang.Long', common_pb2.LONG, None, _long),
-        ('java.lang.Byte', common_pb2.BYTE, None, int),
-        ('java.sql.Time', common_pb2.JAVA_SQL_TIME, time_to_java_sql_time, 
time_from_java_sql_time),
-        ('java.sql.Date', common_pb2.JAVA_SQL_DATE, date_to_java_sql_date, 
date_from_java_sql_date),
-        ('java.sql.Timestamp', common_pb2.JAVA_SQL_TIMESTAMP, 
datetime_to_java_sql_timestamp, datetime_from_java_sql_timestamp),
-    ],
-    'bytes_value': [
-        ('[B', common_pb2.BYTE_STRING, Binary, None),
-    ],
-    'double_value': [
-        # if common_pb2.FLOAT is used, incorrect values are sent
-        ('java.lang.Float', common_pb2.DOUBLE, float, float),
-        ('java.lang.Double', common_pb2.DOUBLE, float, float),
-    ]
-}
-"""Groups of Java classes."""
-
-JAVA_CLASSES_MAP = dict((v[0], (k, v[1], v[2], v[3])) for k in JAVA_CLASSES 
for v in JAVA_CLASSES[k])
-"""Flips the available types to allow for faster lookup by Java class.
-
-This mapping should be structured as:
-    {
-        'java.math.BigDecimal': ('string_value', common_pb2.BIG_DECIMAL, str, 
Decimal),),
-        ...
-        '<java class>': (<field_name>, <Rep enum>, <mutate_to function>, 
<cast_from function>),
-    }
-"""
-
-
-class TypeHelper(object):
-    @staticmethod
-    def from_class(klass):
-        """Retrieves a Rep and functions to cast to/from based on the Java 
class.
-
-        :param klass:
-            The string of the Java class for the column or parameter.
-
-        :returns: tuple ``(field_name, rep, mutate_to, cast_from)``
-            WHERE
-            ``field_name`` is the attribute in ``common_pb2.TypedValue``
-            ``rep`` is the common_pb2.Rep enum
-            ``mutate_to`` is the function to cast values into Phoenix values, 
if any
-            ``cast_from`` is the function to cast from the Phoenix value to 
the Python value, if any
-
-        :raises:
-            NotImplementedError
-        """
-        if klass not in JAVA_CLASSES_MAP:
-            raise NotImplementedError('type {} is not supported'.format(klass))
-
-        return JAVA_CLASSES_MAP[klass]

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/.travis.sh
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/.travis.sh 
b/python/requests-kerberos/.travis.sh
new file mode 100755
index 0000000..a1861d4
--- /dev/null
+++ b/python/requests-kerberos/.travis.sh
@@ -0,0 +1,202 @@
+#!/bin/bash
+
+set -e
+
+IP_ADDRESS=$(hostname -I)
+HOSTNAME=$(cat /etc/hostname)
+PY_MAJOR=${PYENV:0:1}
+
+export KERBEROS_HOSTNAME=$HOSTNAME.$KERBEROS_REALM
+export DEBIAN_FRONTEND=noninteractive
+
+echo "Configure the hosts file for Kerberos to work in a container"
+cp /etc/hosts ~/hosts.new
+sed -i "/.*$HOSTNAME/c\\$IP_ADDRESS\t$KERBEROS_HOSTNAME" ~/hosts.new
+cp -f ~/hosts.new /etc/hosts
+
+echo "Setting up Kerberos config file at /etc/krb5.conf"
+cat > /etc/krb5.conf << EOL
+[libdefaults]
+    default_realm = ${KERBEROS_REALM^^}
+    dns_lookup_realm = false
+    dns_lookup_kdc = false
+
+[realms]
+    ${KERBEROS_REALM^^} = {
+        kdc = $KERBEROS_HOSTNAME
+        admin_server = $KERBEROS_HOSTNAME
+    }
+
+[domain_realm]
+    .$KERBEROS_REALM = ${KERBEROS_REALM^^}
+
+[logging]
+    kdc = FILE:/var/log/krb5kdc.log
+    admin_server = FILE:/var/log/kadmin.log
+    default = FILE:/var/log/krb5lib.log
+EOL
+
+echo "Setting up kerberos ACL configuration at /etc/krb5kdc/kadm5.acl"
+mkdir /etc/krb5kdc
+echo -e "*/*@${KERBEROS_REALM^^}\t*" > /etc/krb5kdc/kadm5.acl
+
+echo "Installing all the packages required in this test"
+apt-get update
+apt-get \
+    -y \
+    -qq \
+    install \
+    krb5-{user,kdc,admin-server,multidev} \
+    libkrb5-dev \
+    wget \
+    curl \
+    apache2 \
+    libapache2-mod-auth-gssapi \
+    python-dev \
+    libffi-dev \
+    build-essential \
+    libssl-dev \
+    zlib1g-dev \
+    libbz2-dev
+
+echo "Creating KDC database"
+# krb5_newrealm returns non-0 return code as it is running in a container, 
ignore it for this command only
+set +e
+printf "$KERBEROS_PASSWORD\n$KERBEROS_PASSWORD" | krb5_newrealm
+set -e
+
+echo "Creating principals for tests"
+kadmin.local -q "addprinc -pw $KERBEROS_PASSWORD $KERBEROS_USERNAME"
+
+echo "Adding HTTP principal for Kerberos and create keytab"
+kadmin.local -q "addprinc -randkey HTTP/$KERBEROS_HOSTNAME"
+kadmin.local -q "ktadd -k /etc/krb5.keytab HTTP/$KERBEROS_HOSTNAME"
+chmod 777 /etc/krb5.keytab
+
+echo "Restarting Kerberos KDS service"
+service krb5-kdc restart
+
+echo "Add ServerName to Apache config"
+grep -q -F "ServerName $KERBEROS_HOSTNAME" /etc/apache2/apache2.conf || echo 
"ServerName $KERBEROS_HOSTNAME" >> /etc/apache2/apache2.conf
+
+echo "Deleting default virtual host file"
+rm /etc/apache2/sites-enabled/000-default.conf
+rm /etc/apache2/sites-available/000-default.conf
+rm /etc/apache2/sites-available/default-ssl.conf
+
+echo "Create website directory structure and pages"
+mkdir -p /var/www/example.com/public_html
+chmod -R 755 /var/www
+echo "<html><head><title>Title</title></head><body>body mesage</body></html>" 
> /var/www/example.com/public_html/index.html
+
+echo "Create self signed certificate for HTTPS endpoint"
+mkdir /etc/apache2/ssl
+openssl req \
+    -x509 \
+    -nodes \
+    -days 365 \
+    -newkey rsa:2048 \
+    -keyout /etc/apache2/ssl/https.key \
+    -out /etc/apache2/ssl/https.crt \
+    -subj "/CN=$KERBEROS_HOSTNAME/o=Testing LTS./C=US"
+
+echo "Create virtual host files"
+cat > /etc/apache2/sites-available/example.com.conf << EOL
+<VirtualHost *:80>
+    ServerName $KERBEROS_HOSTNAME
+    ServerAlias $KERBEROS_HOSTNAME
+    DocumentRoot /var/www/example.com/public_html
+    ErrorLog ${APACHE_LOG_DIR}/error.log
+    CustomLog ${APACHE_LOG_DIR}/access.log combined
+    <Directory "/var/www/example.com/public_html">
+        AuthType GSSAPI
+        AuthName "GSSAPI Single Sign On Login"
+        Require user $KERBEROS_USERNAME@${KERBEROS_REALM^^}
+        GssapiCredStore keytab:/etc/krb5.keytab
+    </Directory>
+</VirtualHost>
+<VirtualHost *:443>
+    ServerName $KERBEROS_HOSTNAME
+    ServerAlias $KERBEROS_HOSTNAME
+    DocumentRoot /var/www/example.com/public_html
+    ErrorLog ${APACHE_LOG_DIR}/error.log
+    CustomLog ${APACHE_LOG_DIR}/access.log combined
+    SSLEngine on
+    SSLCertificateFile /etc/apache2/ssl/https.crt
+    SSLCertificateKeyFile /etc/apache2/ssl/https.key
+    <Directory "/var/www/example.com/public_html">
+        AuthType GSSAPI
+        AuthName "GSSAPI Single Sign On Login"
+        Require user $KERBEROS_USERNAME@${KERBEROS_REALM^^}
+        GssapiCredStore keytab:/etc/krb5.keytab
+    </Directory>
+</VirtualHost>
+EOL
+
+echo "Enabling virtual host site"
+a2enmod ssl
+a2ensite example.com.conf
+service apache2 restart
+
+echo "Getting ticket for Kerberos user"
+echo -n "$KERBEROS_PASSWORD" | kinit "$KERBEROS_USERNAME@${KERBEROS_REALM^^}"
+
+echo "Try out the HTTP connection with curl"
+CURL_OUTPUT=$(curl --negotiate -u : "http://$KERBEROS_HOSTNAME";)
+
+if [ "$CURL_OUTPUT" != "<html><head><title>Title</title></head><body>body 
mesage</body></html>" ]; then
+    echo -e "ERROR: Did not get success message, cannot continue with actual 
tests:\nActual Output:\n$CURL_OUTPUT"
+    exit 1
+else
+    echo -e "SUCCESS: Apache site built and set for Kerberos auth\nActual 
Output:\n$CURL_OUTPUT"
+fi
+
+echo "Try out the HTTPS connection with curl"
+CURL_OUTPUT=$(curl --negotiate -u : "https://$KERBEROS_HOSTNAME"; --insecure)
+
+if [ "$CURL_OUTPUT" != "<html><head><title>Title</title></head><body>body 
mesage</body></html>" ]; then
+    echo -e "ERROR: Did not get success message, cannot continue with actual 
tests:\nActual Output:\n$CURL_OUTPUT"
+    exit 1
+else
+    echo -e "SUCCESS: Apache site built and set for Kerberos auth\nActual 
Output:\n$CURL_OUTPUT"
+fi
+
+if [ "$IMAGE" == "ubuntu:16.04" ]; then
+    echo "Downloading Python $PYENV"
+    wget -q "https://www.python.org/ftp/python/$PYENV/Python-$PYENV.tgz";
+    tar xzf "Python-$PYENV.tgz"
+    cd "Python-$PYENV"
+
+    echo "Configuring Python install"
+    ./configure &> /dev/null
+
+    echo "Running make install on Python"
+    make install &> /dev/null
+    cd ..
+    rm -rf "Python-$PYENV"
+    rm "Python-$PYENV.tgz"
+fi
+
+echo "Installing Pip"
+wget -q https://bootstrap.pypa.io/get-pip.py
+python$PY_MAJOR get-pip.py
+rm get-pip.py
+
+echo "Updating pip and installing library"
+pip$PY_MAJOR install -U pip setuptools
+pip$PY_MAJOR install .
+pip$PY_MAJOR install -r requirements-test.txt
+
+echo "Outputting build info before tests"
+echo "Python Version: $(python$PY_MAJOR --version 2>&1)"
+echo "Pip Version: $(pip$PY_MAJOR --version)"
+echo "Pip packages: $(pip$PY_MAJOR list)"
+
+echo "Running Python tests"
+export KERBEROS_PRINCIPAL="$KERBEROS_USERNAME@${KERBEROS_REALM^^}"
+export KERBEROS_URL="http://$KERBEROS_HOSTNAME";
+python$PY_MAJOR -m pytest -v --cov=requests_kerberos\
+
+echo "Running Python test over HTTPS for basic CBT test"
+export KERBEROS_URL="https://$KERBEROS_HOSTNAME";
+python$PY_MAJOR -m pytest -v --cov=requests_kerberos\

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/.travis.yml
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/.travis.yml 
b/python/requests-kerberos/.travis.yml
new file mode 100644
index 0000000..8253cb4
--- /dev/null
+++ b/python/requests-kerberos/.travis.yml
@@ -0,0 +1,36 @@
+sudo: required
+
+language: python
+
+services:
+- docker
+
+os: linux
+dist: trusty
+
+matrix:
+  include:
+  - env: PYENV=2.7.14 IMAGE=python:2.7.14-slim-stretch
+  - env: PYENV=3.3.7 IMAGE=ubuntu:16.04
+  - env: PYENV=3.4.7 IMAGE=ubuntu:16.04
+  - env: PYENV=3.5.4 IMAGE=ubuntu:16.04
+  - env: PYENV=3.6.3 IMAGE=python:3.6.3-slim-stretch
+
+install:
+- pip install coveralls # Need to have coveralls installed locally for 
after_success to run
+
+script:
+- >
+  docker run
+  -v $(pwd):$(pwd)
+  -w $(pwd)
+  -e PYENV=$PYENV
+  -e IMAGE=$IMAGE
+  -e KERBEROS_USERNAME=administrator
+  -e KERBEROS_PASSWORD=Password01
+  -e KERBEROS_REALM=example.com
+  $IMAGE
+  /bin/bash .travis.sh
+
+after_success:
+- coveralls

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/AUTHORS
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/AUTHORS b/python/requests-kerberos/AUTHORS
new file mode 100644
index 0000000..eae61ac
--- /dev/null
+++ b/python/requests-kerberos/AUTHORS
@@ -0,0 +1,3 @@
+Michael Komitee
+Jose Castro Leon
+David Pursehouse

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/HISTORY.rst
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/HISTORY.rst 
b/python/requests-kerberos/HISTORY.rst
new file mode 100644
index 0000000..44bfc4b
--- /dev/null
+++ b/python/requests-kerberos/HISTORY.rst
@@ -0,0 +1,102 @@
+History
+=======
+
+0.12.0: 2017-12-20
+------------------------
+
+- Add support for channel binding tokens (assumes pykerberos support >= 1.2.1)
+- Add support for kerberos message encryption (assumes pykerberos support >= 
1.2.1)
+- Misc CI/test fixes
+
+0.11.0: 2016-11-02
+------------------
+
+- Switch dependency on Windows from kerberos-sspi/pywin32 to WinKerberos.
+  This brings Custom Principal support to Windows users.
+
+0.10.0: 2016-05-18
+------------------
+
+- Make it possible to receive errors without having their contents and headers
+  stripped.
+- Resolve a bug caused by passing the ``principal`` keyword argument to
+  kerberos-sspi on Windows.
+
+0.9.0: 2016-05-06
+-----------------
+
+- Support for principal, hostname, and realm override.
+
+- Added support for mutual auth.
+
+0.8.0: 2016-01-07
+-----------------
+
+- Support for Kerberos delegation.
+
+- Fixed problems declaring kerberos-sspi on Windows installs.
+
+0.7.0: 2015-05-04
+-----------------
+
+- Added Windows native authentication support by adding kerberos-sspi as an
+  alternative backend.
+
+- Prevent infinite recursion when a server returns 401 to an authorization
+  attempt.
+
+- Reduce the logging during successful responses.
+
+0.6.1: 2014-11-14
+-----------------
+
+- Fix HTTPKerberosAuth not to treat non-file as a file
+
+- Prevent infinite recursion when GSSErrors occurs
+
+0.6: 2014-11-04
+---------------
+
+- Handle mutual authentication (see pull request 36_)
+
+  All users should upgrade immediately. This has been reported to
+  oss-security_ and we are awaiting a proper CVE identifier.
+
+  **Update**: We were issued CVE-2014-8650
+
+- Distribute as a wheel.
+
+.. _36: https://github.com/requests/requests-kerberos/pull/36
+.. _oss-security: http://www.openwall.com/lists/oss-security/
+
+0.5: 2014-05-14
+---------------
+
+- Allow non-HTTP service principals with HTTPKerberosAuth using a new optional
+  argument ``service``.
+
+- Fix bug in ``setup.py`` on distributions where the ``compiler`` module is
+  not available.
+
+- Add test dependencies to ``setup.py`` so ``python setup.py test`` will work.
+
+0.4: 2013-10-26
+---------------
+
+- Minor updates in the README
+- Change requirements to depend on requests above 1.1.0
+
+0.3: 2013-06-02
+---------------
+
+- Work with servers operating on non-standard ports
+
+0.2: 2013-03-26
+---------------
+
+- Not documented
+
+0.1: Never released
+-------------------
+
+- Initial Release

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/LICENSE
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/LICENSE b/python/requests-kerberos/LICENSE
new file mode 100644
index 0000000..581f115
--- /dev/null
+++ b/python/requests-kerberos/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2012 Kenneth Reitz
+
+Permission to use, copy, modify and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS-IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/MANIFEST.in
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/MANIFEST.in 
b/python/requests-kerberos/MANIFEST.in
new file mode 100644
index 0000000..c5c480f
--- /dev/null
+++ b/python/requests-kerberos/MANIFEST.in
@@ -0,0 +1,5 @@
+include requirements.txt
+include README.rst
+include LICENSE
+include HISTORY.rst
+include AUTHORS

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/README.rst
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/README.rst 
b/python/requests-kerberos/README.rst
new file mode 100644
index 0000000..c9bac7c
--- /dev/null
+++ b/python/requests-kerberos/README.rst
@@ -0,0 +1,173 @@
+requests Kerberos/GSSAPI authentication library
+===============================================
+
+.. image:: https://travis-ci.org/requests/requests-kerberos.svg?branch=master
+    :target: https://travis-ci.org/requests/requests-kerberos
+
+.. image:: 
https://coveralls.io/repos/github/requests/requests-kerberos/badge.svg?branch=master
+    :target: 
https://coveralls.io/github/requests/requests-kerberos?branch=master
+
+Requests is an HTTP library, written in Python, for human beings. This library
+adds optional Kerberos/GSSAPI authentication support and supports mutual
+authentication. Basic GET usage:
+
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth
+    >>> r = requests.get("http://example.org";, auth=HTTPKerberosAuth())
+    ...
+
+The entire ``requests.api`` should be supported.
+
+Authentication Failures
+-----------------------
+
+Client authentication failures will be communicated to the caller by returning
+the 401 response.
+
+Mutual Authentication
+---------------------
+
+REQUIRED
+^^^^^^^^
+
+By default, ``HTTPKerberosAuth`` will require mutual authentication from the
+server, and if a server emits a non-error response which cannot be
+authenticated, a ``requests_kerberos.errors.MutualAuthenticationError`` will
+be raised. If a server emits an error which cannot be authenticated, it will
+be returned to the user but with its contents and headers stripped. If the
+response content is more important than the need for mutual auth on errors,
+(eg, for certain WinRM calls) the stripping behavior can be suppressed by
+setting ``sanitize_mutual_error_response=False``:
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth, REQUIRED
+    >>> kerberos_auth = HTTPKerberosAuth(mutual_authentication=REQUIRED, 
sanitize_mutual_error_response=False)
+    >>> r = requests.get("https://windows.example.org/wsman";, 
auth=kerberos_auth)
+    ...
+
+
+OPTIONAL
+^^^^^^^^
+
+If you'd prefer to not require mutual authentication, you can set your
+preference when constructing your ``HTTPKerberosAuth`` object:
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth, OPTIONAL
+    >>> kerberos_auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
+    >>> r = requests.get("http://example.org";, auth=kerberos_auth)
+    ...
+
+This will cause ``requests_kerberos`` to attempt mutual authentication if the
+server advertises that it supports it, and cause a failure if authentication
+fails, but not if the server does not support it at all.
+
+DISABLED
+^^^^^^^^
+
+While we don't recommend it, if you'd prefer to never attempt mutual
+authentication, you can do that as well:
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth, DISABLED
+    >>> kerberos_auth = HTTPKerberosAuth(mutual_authentication=DISABLED)
+    >>> r = requests.get("http://example.org";, auth=kerberos_auth)
+    ...
+
+Preemptive Authentication
+-------------------------
+
+``HTTPKerberosAuth`` can be forced to preemptively initiate the Kerberos
+GSS exchange and present a Kerberos ticket on the initial request (and all
+subsequent). By default, authentication only occurs after a
+``401 Unauthorized`` response containing a Kerberos or Negotiate challenge
+is received from the origin server. This can cause mutual authentication
+failures for hosts that use a persistent connection (eg, Windows/WinRM), as
+no Kerberos challenges are sent after the initial auth handshake. This
+behavior can be altered by setting  ``force_preemptive=True``:
+
+.. code-block:: python
+    
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth, REQUIRED
+    >>> kerberos_auth = HTTPKerberosAuth(mutual_authentication=REQUIRED, 
force_preemptive=True)
+    >>> r = requests.get("https://windows.example.org/wsman";, 
auth=kerberos_auth)
+    ...
+
+Hostname Override
+-----------------
+
+If communicating with a host whose DNS name doesn't match its
+kerberos hostname (eg, behind a content switch or load balancer),
+the hostname used for the Kerberos GSS exchange can be overridden by
+setting the ``hostname_override`` arg:
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth, REQUIRED
+    >>> kerberos_auth = 
HTTPKerberosAuth(hostname_override="internalhost.local")
+    >>> r = requests.get("https://externalhost.example.org/";, 
auth=kerberos_auth)
+    ...
+
+Explicit Principal
+------------------
+
+``HTTPKerberosAuth`` normally uses the default principal (ie, the user for
+whom you last ran ``kinit`` or ``kswitch``, or an SSO credential if
+applicable). However, an explicit principal can be specified, which will
+cause Kerberos to look for a matching credential cache for the named user.
+This feature depends on OS support for collection-type credential caches,
+as well as working principal support in PyKerberos (it is broken in many
+builds). An explicit principal can be specified with the ``principal`` arg:
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth, REQUIRED
+    >>> kerberos_auth = HTTPKerberosAuth(principal="user@REALM")
+    >>> r = requests.get("http://example.org";, auth=kerberos_auth)
+    ...
+
+On Windows, WinKerberos is used instead of PyKerberos. WinKerberos allows the
+use of arbitrary principals instead of a credential cache. Passwords can be
+specified by following the form ``user@realm:password`` for ``principal``.
+
+Delegation
+----------
+
+``requests_kerberos`` supports credential delegation (``GSS_C_DELEG_FLAG``).
+To enable delegation of credentials to a server that requests delegation, pass
+``delegate=True`` to ``HTTPKerberosAuth``:
+
+.. code-block:: python
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth
+    >>> r = requests.get("http://example.org";, 
auth=HTTPKerberosAuth(delegate=True))
+    ...
+
+Be careful to only allow delegation to servers you trust as they will be able
+to impersonate you using the delegated credentials.
+
+Logging
+-------
+
+This library makes extensive use of Python's logging facilities.
+
+Log messages are logged to the ``requests_kerberos`` and
+``requests_kerberos.kerberos_`` named loggers.
+
+If you are having difficulty we suggest you configure logging. Issues with the
+underlying kerberos libraries will be made apparent. Additionally, copious 
debug
+information is made available which may assist in troubleshooting if you
+increase your log level all the way up to debug.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/requests_kerberos/__init__.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/requests_kerberos/__init__.py 
b/python/requests-kerberos/requests_kerberos/__init__.py
new file mode 100644
index 0000000..63c7db5
--- /dev/null
+++ b/python/requests-kerberos/requests_kerberos/__init__.py
@@ -0,0 +1,25 @@
+"""
+requests Kerberos/GSSAPI authentication library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Requests is an HTTP library, written in Python, for human beings. This library
+adds optional Kerberos/GSSAPI authentication support and supports mutual
+authentication. Basic GET usage:
+
+    >>> import requests
+    >>> from requests_kerberos import HTTPKerberosAuth
+    >>> r = requests.get("http://example.org";, auth=HTTPKerberosAuth())
+
+The entire `requests.api` should be supported.
+"""
+import logging
+
+from .kerberos_ import HTTPKerberosAuth, REQUIRED, OPTIONAL, DISABLED
+from .exceptions import MutualAuthenticationError
+from .compat import NullHandler
+
+logging.getLogger(__name__).addHandler(NullHandler())
+
+__all__ = ('HTTPKerberosAuth', 'MutualAuthenticationError', 'REQUIRED',
+           'OPTIONAL', 'DISABLED')
+__version__ = '0.13.0.dev0'

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/requests_kerberos/compat.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/requests_kerberos/compat.py 
b/python/requests-kerberos/requests_kerberos/compat.py
new file mode 100644
index 0000000..01b7500
--- /dev/null
+++ b/python/requests-kerberos/requests_kerberos/compat.py
@@ -0,0 +1,14 @@
+"""
+Compatibility library for older versions of python
+"""
+import sys
+
+# python 2.7 introduced a NullHandler which we want to use, but to support
+# older versions, we implement our own if needed.
+if sys.version_info[:2] > (2, 6):
+    from logging import NullHandler
+else:
+    from logging import Handler
+    class NullHandler(Handler):
+        def emit(self, record):
+            pass

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/requests_kerberos/exceptions.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/requests_kerberos/exceptions.py 
b/python/requests-kerberos/requests_kerberos/exceptions.py
new file mode 100644
index 0000000..51e11ec
--- /dev/null
+++ b/python/requests-kerberos/requests_kerberos/exceptions.py
@@ -0,0 +1,15 @@
+"""
+requests_kerberos.exceptions
+~~~~~~~~~~~~~~~~~~~
+
+This module contains the set of exceptions.
+
+"""
+from requests.exceptions import RequestException
+
+
+class MutualAuthenticationError(RequestException):
+    """Mutual Authentication Error"""
+
+class KerberosExchangeError(RequestException):
+    """Kerberos Exchange Failed Error"""

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/requests_kerberos/kerberos_.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/requests_kerberos/kerberos_.py 
b/python/requests-kerberos/requests_kerberos/kerberos_.py
new file mode 100644
index 0000000..38f9f76
--- /dev/null
+++ b/python/requests-kerberos/requests_kerberos/kerberos_.py
@@ -0,0 +1,457 @@
+try:
+    import kerberos
+except ImportError:
+    import winkerberos as kerberos
+import logging
+import re
+import sys
+import warnings
+
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.exceptions import UnsupportedAlgorithm
+
+from requests.auth import AuthBase
+from requests.models import Response
+from requests.compat import urlparse, StringIO
+from requests.structures import CaseInsensitiveDict
+from requests.cookies import cookiejar_from_dict
+from requests.packages.urllib3 import HTTPResponse
+
+from .exceptions import MutualAuthenticationError, KerberosExchangeError
+
+log = logging.getLogger(__name__)
+
+# Different types of mutual authentication:
+#  with mutual_authentication set to REQUIRED, all responses will be
+#   authenticated with the exception of errors. Errors will have their contents
+#   and headers stripped. If a non-error response cannot be authenticated, a
+#   MutualAuthenticationError exception will be raised.
+# with mutual_authentication set to OPTIONAL, mutual authentication will be
+#   attempted if supported, and if supported and failed, a
+#   MutualAuthenticationError exception will be raised. Responses which do not
+#   support mutual authentication will be returned directly to the user.
+# with mutual_authentication set to DISABLED, mutual authentication will not be
+#   attempted, even if supported.
+REQUIRED = 1
+OPTIONAL = 2
+DISABLED = 3
+
+
+class NoCertificateRetrievedWarning(Warning):
+    pass
+
+class UnknownSignatureAlgorithmOID(Warning):
+    pass
+
+
+class SanitizedResponse(Response):
+    """The :class:`Response <Response>` object, which contains a server's
+    response to an HTTP request.
+
+    This differs from `requests.models.Response` in that it's headers and
+    content have been sanitized. This is only used for HTTP Error messages
+    which do not support mutual authentication when mutual authentication is
+    required."""
+
+    def __init__(self, response):
+        super(SanitizedResponse, self).__init__()
+        self.status_code = response.status_code
+        self.encoding = response.encoding
+        self.raw = response.raw
+        self.reason = response.reason
+        self.url = response.url
+        self.request = response.request
+        self.connection = response.connection
+        self._content_consumed = True
+
+        self._content = ""
+        self.cookies = cookiejar_from_dict({})
+        self.headers = CaseInsensitiveDict()
+        self.headers['content-length'] = '0'
+        for header in ('date', 'server'):
+            if header in response.headers:
+                self.headers[header] = response.headers[header]
+
+
+def _negotiate_value(response):
+    """Extracts the gssapi authentication token from the appropriate header"""
+    if hasattr(_negotiate_value, 'regex'):
+        regex = _negotiate_value.regex
+    else:
+        # There's no need to re-compile this EVERY time it is called. Compile
+        # it once and you won't have the performance hit of the compilation.
+        regex = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I)
+        _negotiate_value.regex = regex
+
+    authreq = response.headers.get('www-authenticate', None)
+
+    if authreq:
+        match_obj = regex.search(authreq)
+        if match_obj:
+            return match_obj.group(1)
+
+    return None
+
+
+def _get_certificate_hash(certificate_der):
+    # https://tools.ietf.org/html/rfc5929#section-4.1
+    cert = x509.load_der_x509_certificate(certificate_der, default_backend())
+
+    try:
+        hash_algorithm = cert.signature_hash_algorithm
+    except UnsupportedAlgorithm as ex:
+        warnings.warn("Failed to get signature algorithm from certificate, "
+                      "unable to pass channel bindings: %s" % str(ex), 
UnknownSignatureAlgorithmOID)
+        return None
+
+    # if the cert signature algorithm is either md5 or sha1 then use sha256
+    # otherwise use the signature algorithm
+    if hash_algorithm.name in ['md5', 'sha1']:
+        digest = hashes.Hash(hashes.SHA256(), default_backend())
+    else:
+        digest = hashes.Hash(hash_algorithm, default_backend())
+
+    digest.update(certificate_der)
+    certificate_hash = digest.finalize()
+
+    return certificate_hash
+
+
+def _get_channel_bindings_application_data(response):
+    """
+    https://tools.ietf.org/html/rfc5929 4. The 'tls-server-end-point' Channel 
Binding Type
+
+    Gets the application_data value for the 'tls-server-end-point' CBT Type.
+    This is ultimately the SHA256 hash of the certificate of the HTTPS endpoint
+    appended onto tls-server-end-point. This value is then passed along to the
+    kerberos library to bind to the auth response. If the socket is not an SSL
+    socket or the raw HTTP object is not a urllib3 HTTPResponse then None will
+    be returned and the Kerberos auth will use GSS_C_NO_CHANNEL_BINDINGS
+
+    :param response: The original 401 response from the server
+    :return: byte string used on the application_data.value field on the CBT 
struct
+    """
+
+    application_data = None
+    raw_response = response.raw
+
+    if isinstance(raw_response, HTTPResponse):
+        try:
+            if sys.version_info > (3, 0):
+                socket = raw_response._fp.fp.raw._sock
+            else:
+                socket = raw_response._fp.fp._sock
+        except AttributeError:
+            warnings.warn("Failed to get raw socket for CBT; has urllib3 impl 
changed",
+                          NoCertificateRetrievedWarning)
+        else:
+            try:
+                server_certificate = socket.getpeercert(True)
+            except AttributeError:
+                pass
+            else:
+                certificate_hash = _get_certificate_hash(server_certificate)
+                application_data = b'tls-server-end-point:' + certificate_hash
+    else:
+        warnings.warn(
+            "Requests is running with a non urllib3 backend, cannot retrieve 
server certificate for CBT",
+            NoCertificateRetrievedWarning)
+
+    return application_data
+
+class HTTPKerberosAuth(AuthBase):
+    """Attaches HTTP GSSAPI/Kerberos Authentication to the given Request
+    object."""
+    def __init__(
+            self, mutual_authentication=REQUIRED,
+            service="HTTP", delegate=False, force_preemptive=False,
+            principal=None, hostname_override=None, mech_oid=None,
+            sanitize_mutual_error_response=True, send_cbt=True):
+        self.context = {}
+        self.mutual_authentication = mutual_authentication
+        self.delegate = delegate
+        self.pos = None
+        self.service = service
+        self.force_preemptive = force_preemptive
+        self.principal = principal
+        self.hostname_override = hostname_override
+        self.mech_oid = mech_oid
+        self.sanitize_mutual_error_response = sanitize_mutual_error_response
+        self.auth_done = False
+        self.winrm_encryption_available = hasattr(kerberos, 
'authGSSWinRMEncryptMessage')
+
+        # Set the CBT values populated after the first response
+        self.send_cbt = send_cbt
+        self.cbt_binding_tried = False
+        self.cbt_struct = None
+
+    def generate_request_header(self, response, host, is_preemptive=False):
+        """
+        Generates the GSSAPI authentication token with kerberos.
+
+        If any GSSAPI step fails, raise KerberosExchangeError
+        with failure detail.
+
+        """
+
+        # Flags used by kerberos module.
+        gssflags = kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG
+        if self.delegate:
+            gssflags |= kerberos.GSS_C_DELEG_FLAG
+
+        try:
+            kerb_stage = "authGSSClientInit()"
+            # contexts still need to be stored by host, but hostname_override
+            # allows use of an arbitrary hostname for the kerberos exchange
+            # (eg, in cases of aliased hosts, internal vs external, CNAMEs
+            # w/ name-based HTTP hosting)
+            kerb_host = self.hostname_override if self.hostname_override is 
not None else host
+            kerb_spn = "{0}@{1}".format(self.service, kerb_host)
+
+            if self.mech_oid is not None: 
+                result, self.context[host] = 
kerberos.authGSSClientInit(kerb_spn,
+                    gssflags=gssflags, principal=self.principal, 
mech_oid=self.mech_oid)
+            else:
+                result, self.context[host] = 
kerberos.authGSSClientInit(kerb_spn,
+                    gssflags=gssflags, principal=self.principal)
+
+            if result < 1:
+                raise EnvironmentError(result, kerb_stage)
+
+            # if we have a previous response from the server, use it to 
continue
+            # the auth process, otherwise use an empty value
+            negotiate_resp_value = '' if is_preemptive else 
_negotiate_value(response)
+
+            kerb_stage = "authGSSClientStep()"
+            # If this is set pass along the struct to Kerberos
+            if self.cbt_struct:
+                result = kerberos.authGSSClientStep(self.context[host],
+                                                    negotiate_resp_value,
+                                                    
channel_bindings=self.cbt_struct)
+            else:
+                result = kerberos.authGSSClientStep(self.context[host],
+                                                    negotiate_resp_value)
+
+            if result < 0:
+                raise EnvironmentError(result, kerb_stage)
+
+            kerb_stage = "authGSSClientResponse()"
+            gss_response = kerberos.authGSSClientResponse(self.context[host])
+
+            return "Negotiate {0}".format(gss_response)
+
+        except kerberos.GSSError as error:
+            log.exception(
+                "generate_request_header(): {0} failed:".format(kerb_stage))
+            log.exception(error)
+            raise KerberosExchangeError("%s failed: %s" % (kerb_stage, 
str(error.args)))
+
+        except EnvironmentError as error:
+            # ensure we raised this for translation to KerberosExchangeError
+            # by comparing errno to result, re-raise if not
+            if error.errno != result:
+                raise
+            message = "{0} failed, result: {1}".format(kerb_stage, result)
+            log.error("generate_request_header(): {0}".format(message))
+            raise KerberosExchangeError(message)
+
+    def authenticate_user(self, response, **kwargs):
+        """Handles user authentication with gssapi/kerberos"""
+
+        host = urlparse(response.url).hostname
+
+        try:
+            auth_header = self.generate_request_header(response, host)
+        except KerberosExchangeError:
+            # GSS Failure, return existing response
+            return response
+
+        log.debug("authenticate_user(): Authorization header: {0}".format(
+            auth_header))
+        response.request.headers['Authorization'] = auth_header
+
+        # Consume the content so we can reuse the connection for the next
+        # request.
+        response.content
+        response.raw.release_conn()
+
+        _r = response.connection.send(response.request, **kwargs)
+        _r.history.append(response)
+
+        log.debug("authenticate_user(): returning {0}".format(_r))
+        return _r
+
+    def handle_401(self, response, **kwargs):
+        """Handles 401's, attempts to use gssapi/kerberos authentication"""
+
+        log.debug("handle_401(): Handling: 401")
+        if _negotiate_value(response) is not None:
+            _r = self.authenticate_user(response, **kwargs)
+            log.debug("handle_401(): returning {0}".format(_r))
+            return _r
+        else:
+            log.debug("handle_401(): Kerberos is not supported")
+            log.debug("handle_401(): returning {0}".format(response))
+            return response
+
+    def handle_other(self, response):
+        """Handles all responses with the exception of 401s.
+
+        This is necessary so that we can authenticate responses if requested"""
+
+        log.debug("handle_other(): Handling: %d" % response.status_code)
+
+        if self.mutual_authentication in (REQUIRED, OPTIONAL) and not 
self.auth_done:
+
+            is_http_error = response.status_code >= 400
+
+            if _negotiate_value(response) is not None:
+                log.debug("handle_other(): Authenticating the server")
+                if not self.authenticate_server(response):
+                    # Mutual authentication failure when mutual auth is wanted,
+                    # raise an exception so the user doesn't use an untrusted
+                    # response.
+                    log.error("handle_other(): Mutual authentication failed")
+                    raise MutualAuthenticationError("Unable to authenticate "
+                                                    "{0}".format(response))
+
+                # Authentication successful
+                log.debug("handle_other(): returning {0}".format(response))
+                self.auth_done = True
+                return response
+
+            elif is_http_error or self.mutual_authentication == OPTIONAL:
+                if not response.ok:
+                    log.error("handle_other(): Mutual authentication 
unavailable "
+                              "on {0} response".format(response.status_code))
+
+                if(self.mutual_authentication == REQUIRED and
+                       self.sanitize_mutual_error_response):
+                    return SanitizedResponse(response)
+                else:
+                    return response
+            else:
+                # Unable to attempt mutual authentication when mutual auth is
+                # required, raise an exception so the user doesn't use an
+                # untrusted response.
+                log.error("handle_other(): Mutual authentication failed")
+                raise MutualAuthenticationError("Unable to authenticate "
+                                                "{0}".format(response))
+        else:
+            log.debug("handle_other(): returning {0}".format(response))
+            return response
+
+    def authenticate_server(self, response):
+        """
+        Uses GSSAPI to authenticate the server.
+
+        Returns True on success, False on failure.
+        """
+
+        log.debug("authenticate_server(): Authenticate header: {0}".format(
+            _negotiate_value(response)))
+
+        host = urlparse(response.url).hostname
+
+        try:
+            # If this is set pass along the struct to Kerberos
+            if self.cbt_struct:
+                result = kerberos.authGSSClientStep(self.context[host],
+                                                    _negotiate_value(response),
+                                                    
channel_bindings=self.cbt_struct)
+            else:
+                result = kerberos.authGSSClientStep(self.context[host],
+                                                    _negotiate_value(response))
+        except kerberos.GSSError:
+            log.exception("authenticate_server(): authGSSClientStep() failed:")
+            return False
+
+        if result < 1:
+            log.error("authenticate_server(): authGSSClientStep() failed: "
+                      "{0}".format(result))
+            return False
+
+        log.debug("authenticate_server(): returning {0}".format(response))
+        return True
+
+    def handle_response(self, response, **kwargs):
+        """Takes the given response and tries kerberos-auth, as needed."""
+        num_401s = kwargs.pop('num_401s', 0)
+
+        # Check if we have already tried to get the CBT data value
+        if not self.cbt_binding_tried and self.send_cbt:
+            # If we haven't tried, try getting it now
+            cbt_application_data = 
_get_channel_bindings_application_data(response)
+            if cbt_application_data:
+                # Only the latest version of pykerberos has this method 
available
+                try:
+                    self.cbt_struct = 
kerberos.channelBindings(application_data=cbt_application_data)
+                except AttributeError:
+                    # Using older version set to None
+                    self.cbt_struct = None
+            # Regardless of the result, set tried to True so we don't waste 
time next time
+            self.cbt_binding_tried = True
+
+        if self.pos is not None:
+            # Rewind the file position indicator of the body to where
+            # it was to resend the request.
+            response.request.body.seek(self.pos)
+
+        if response.status_code == 401 and num_401s < 2:
+            # 401 Unauthorized. Handle it, and if it still comes back as 401,
+            # that means authentication failed.
+            _r = self.handle_401(response, **kwargs)
+            log.debug("handle_response(): returning %s", _r)
+            log.debug("handle_response() has seen %d 401 responses", num_401s)
+            num_401s += 1
+            return self.handle_response(_r, num_401s=num_401s, **kwargs)
+        elif response.status_code == 401 and num_401s >= 2:
+            # Still receiving 401 responses after attempting to handle them.
+            # Authentication has failed. Return the 401 response.
+            log.debug("handle_response(): returning 401 %s", response)
+            return response
+        else:
+            _r = self.handle_other(response)
+            log.debug("handle_response(): returning %s", _r)
+            return _r
+
+    def deregister(self, response):
+        """Deregisters the response handler"""
+        response.request.deregister_hook('response', self.handle_response)
+
+    def wrap_winrm(self, host, message):
+        if not self.winrm_encryption_available:
+            raise NotImplementedError("WinRM encryption is not available on 
the installed version of pykerberos")
+
+        return kerberos.authGSSWinRMEncryptMessage(self.context[host], message)
+
+    def unwrap_winrm(self, host, message, header):
+        if not self.winrm_encryption_available:
+            raise NotImplementedError("WinRM encryption is not available on 
the installed version of pykerberos")
+
+        return kerberos.authGSSWinRMDecryptMessage(self.context[host], 
message, header)
+
+    def __call__(self, request):
+        if self.force_preemptive and not self.auth_done:
+            # add Authorization header before we receive a 401
+            # by the 401 handler
+            host = urlparse(request.url).hostname
+
+            auth_header = self.generate_request_header(None, host, 
is_preemptive=True)
+
+            log.debug("HTTPKerberosAuth: Preemptive Authorization header: 
{0}".format(auth_header))
+
+            request.headers['Authorization'] = auth_header
+
+        request.register_hook('response', self.handle_response)
+        try:
+            self.pos = request.body.tell()
+        except AttributeError:
+            # In the case of HTTPKerberosAuth being reused and the body
+            # of the previous request was a file-like object, pos has
+            # the file position of the previous body. Ensure it's set to
+            # None.
+            self.pos = None
+        return request

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/requirements-test.txt
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/requirements-test.txt 
b/python/requests-kerberos/requirements-test.txt
new file mode 100644
index 0000000..88c224f
--- /dev/null
+++ b/python/requests-kerberos/requirements-test.txt
@@ -0,0 +1,4 @@
+mock
+pytest<=3.2.5
+pytest-cov
+coveralls

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/requirements.txt
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/requirements.txt 
b/python/requests-kerberos/requirements.txt
new file mode 100644
index 0000000..ebb2686
--- /dev/null
+++ b/python/requests-kerberos/requirements.txt
@@ -0,0 +1,6 @@
+requests>=1.1.0
+winkerberos >= 0.5.0; sys.platform == 'win32'
+pykerberos >= 1.1.8, < 1.2.0; sys.platform != 'win32'
+cryptography>=1.3
+cryptography>=1.3; python_version!="3.3"
+cryptography>=1.3, <2; python_version=="3.3"

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/setup.cfg
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/setup.cfg 
b/python/requests-kerberos/setup.cfg
new file mode 100644
index 0000000..5e40900
--- /dev/null
+++ b/python/requests-kerberos/setup.cfg
@@ -0,0 +1,2 @@
+[wheel]
+universal = 1

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/setup.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/setup.py 
b/python/requests-kerberos/setup.py
new file mode 100755
index 0000000..ee89ced
--- /dev/null
+++ b/python/requests-kerberos/setup.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# coding: utf-8
+import os
+import re
+from setuptools import setup
+
+path = os.path.dirname(__file__)
+desc_fd = os.path.join(path, 'README.rst')
+hist_fd = os.path.join(path, 'HISTORY.rst')
+
+long_desc = ''
+short_desc = 'A Kerberos authentication handler for python-requests'
+
+if os.path.isfile(desc_fd):
+    with open(desc_fd) as fd:
+        long_desc = fd.read()
+
+if os.path.isfile(hist_fd):
+    with open(hist_fd) as fd:
+        long_desc = '\n\n'.join([long_desc, fd.read()])
+
+
+def get_version():
+    """
+    Simple function to extract the current version using regular expressions.
+    """
+    reg = re.compile(r'__version__ = [\'"]([^\'"]*)[\'"]')
+    with open('requests_kerberos/__init__.py') as fd:
+        matches = list(filter(lambda x: x, map(reg.match, fd)))
+
+    if not matches:
+        raise RuntimeError(
+            'Could not find the version information for requests_kerberos'
+            )
+
+    return matches[0].group(1)
+
+
+setup(
+    name='requests-kerberos',
+    description=short_desc,
+    long_description=long_desc,
+    author='Ian Cordasco, Cory Benfield, Michael Komitee',
+    author_email='graffatcolmin...@gmail.com',
+    url='https://github.com/requests/requests-kerberos',
+    packages=['requests_kerberos'],
+    package_data={'': ['LICENSE', 'AUTHORS']},
+    include_package_data=True,
+    version=get_version(),
+    install_requires=[
+        'requests>=1.1.0',
+        'cryptography>=1.3;python_version!="3.3"',
+        'cryptography>=1.3,<2;python_version=="3.3"'
+    ],
+    extras_require={
+        ':sys_platform=="win32"': ['winkerberos>=0.5.0'],
+        ':sys_platform!="win32"': ['pykerberos>=1.1.8,<1.2.0'],
+    },
+    test_suite='test_requests_kerberos',
+    tests_require=['mock'],
+    classifiers=[
+        "License :: OSI Approved :: ISC License (ISCL)"
+    ],
+)

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/tests/__init__.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/tests/__init__.py 
b/python/requests-kerberos/tests/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e62be9c8/python/requests-kerberos/tests/test_functional_kerberos.py
----------------------------------------------------------------------
diff --git a/python/requests-kerberos/tests/test_functional_kerberos.py 
b/python/requests-kerberos/tests/test_functional_kerberos.py
new file mode 100644
index 0000000..a3efef4
--- /dev/null
+++ b/python/requests-kerberos/tests/test_functional_kerberos.py
@@ -0,0 +1,47 @@
+import requests
+import os
+import unittest
+
+from requests_kerberos import HTTPKerberosAuth, REQUIRED
+
+
+class KerberosFunctionalTestCase(unittest.TestCase):
+    """
+    This test is designed to run functional tests against a live website
+    secured with Kerberos authentication. See .travis.sh for the script that
+    is used to setup a Kerberos realm and Apache site.
+        
+    For this test to run the 2 environment variables need to be set
+        KERBEROS_PRINCIPAL: The principal to authenticate with (u...@realm.com)
+            Before running this test you need to ensure you have gotten a valid
+            ticket for the user in that realm using kinit.
+        KERBEROS_URL: The URL (http://host.realm.com) to authenticate with
+            This need to be set up before hand          
+    """
+
+    def setUp(self):
+        """Setup."""
+        self.principal = os.environ.get('KERBEROS_PRINCIPAL', None)
+        self.url = os.environ.get('KERBEROS_URL', None)
+
+        # Skip the test if not set
+        if self.principal is None:
+            raise unittest.SkipTest("KERBEROS_PRINCIPAL is not set, skipping 
functional tests")
+        if self.url is None:
+            raise unittest.SkipTest("KERBEROS_URL is not set, skipping 
functional tests")
+
+    def test_successful_http_call(self):
+        session = requests.Session()
+        if self.url.startswith("https://";):
+            session.verify = False
+
+        session.auth = HTTPKerberosAuth(mutual_authentication=REQUIRED, 
principal=self.principal)
+        request = requests.Request('GET', self.url)
+        prepared_request = session.prepare_request(request)
+
+        response = session.send(prepared_request)
+
+        assert response.status_code == 200, "HTTP response with kerberos auth 
did not return a 200 error code"
+
+if __name__ == '__main__':
+    unittest.main()

Reply via email to