Azure / msrest-for-python

The runtime library "msrest" for AutoRest generated Python clients.
MIT License
41 stars 64 forks source link

Support for Kerberos? #179

Closed catatonicprime closed 4 years ago

catatonicprime commented 5 years ago

Using kerberos would be extremely useful for riding on one's Single Sign On credentials in domain joined environments to help manage REST services which might already include the consumption of kerberos identities.

I'm no expert in these areas, for sure, but luckily I like to collect from others.

Here's what I've done to get kerberos working in one case but I don't think this is a comprehensive/complete solution that merits a PR just yet. In this case I'm working with azure-devops as a REST server:

  1. CentOS7 installation
  2. Joined to domain using realmd
  3. kinit to get a ticket
  4. pip install --user requests_kerberos (provided here: https://github.com/requests/requests-kerberos)
  5. pip install --user azure-devops (provided here: https://github.com/Microsoft/azure-devops-python-api)
  6. Create the example file, with some modifications:
from azure.devops.connection import Connection
from msrest.authentication import BasicAuthentication
from msrest.authentication import Authentication
from requests_kerberos import HTTPKerberosAuth # Added prereq.
import pprint

# Added Class
class KerberosAuth(HTTPKerberosAuth, Authentication):
    def signed_session(self, session=None):
        session.auth = HTTPKerberosAuth()
        return session

# Fill in with your personal access token and org URL

organization_url = 'https://dev.azure.com/YOURORG' # Point this at your azure-devops instance

# Create a connection to the org
credentials =  KerberosAuth() # grabs your kerberos tickets
connection = Connection(base_url=organization_url, creds=credentials)

# Get a client (the "core" client provides access to projects, teams, etc)
core_client = connection.clients.get_core_client()

# Get the first page of projects
get_projects_response = core_client.get_projects()
index = 0
while get_projects_response is not None:
    for project in get_projects_response.value:
        pprint.pprint("[" + str(index) + "] " + project.name)
        index += 1
    if get_projects_response.continuation_token is not None and get_projects_response.continuation_token != "":
        # Get the next page of projects
        get_projects_response = core_client.get_projects(continuation_token=get_projects_response.continuation_token)
    else:
        # All projects have been retrieved
        get_projects_response = None

This worked great for me - but I'm not sure what limitations something like this has. Simply it's kind of a hack that works well enough for what I need but it seems like something that may be able to be integrated properly somehow.

What does the team think?

lmazuel commented 4 years ago

Hi @catatonicprime

I think that's a nice idea, could you provide a PR for that?

I can't add a new dependency, too much impact for a low usage, but I would suggest this code:

class KerberosAuth(Authentication):
    def signed_session(self, session=None):
        session = super(KerberosAuth, self).signed_session(session)
        try:
            from requests_kerberos import HTTPKerberosAuth
        except ImportError:
            raise ImportError("In order to use KerberosAuth please do 'pip install requests_kerberos' first")    
        session.auth = HTTPKerberosAuth()
        return session

If you can't that's fine, this issue can become the "official" sample for that :)

lmazuel commented 4 years ago

Merged