store GSSAPI token in flask.g

pull/13/head
Max Erenberg 1 year ago
parent d8e5b1f1d4
commit 7d23fd690f
  1. 4
      ceo_common/model/HTTPClient.py
  2. 6
      ceod/api/spnego.py
  3. 6
      ceod/model/LDAPService.py
  4. 20
      tests/conftest.py
  5. 4
      tests/conftest_ceo.py
  6. 6
      tests/conftest_ceod_api.py
  7. 4
      tests/utils.py

@ -30,10 +30,10 @@ class HTTPClient:
'opportunistic_auth': True,
'target_name': gssapi.Name('ceod/' + host),
}
if flask.has_request_context() and 'client_creds' in flask.g:
if flask.has_request_context() and 'client_token' in flask.g:
# This is reached when we are the server and the client has forwarded
# their credentials to us.
spnego_kwargs['creds'] = flask.g.client_creds
spnego_kwargs['creds'] = gssapi.Credentials(token=flask.g.client_token)
if delegate:
# This is reached when we are the client and we want to forward our
# credentials to the server.

@ -52,7 +52,11 @@ def requires_authentication(f):
# Store the delegated credentials, if they were given
if ctx.actual_flags & RequirementFlag.delegate_to_peer:
g.client_creds = ctx.delegated_creds
# For some reason, shit gets screwed up when you try to use a
# gssapi.Credentials object which was created in another function.
# So we're going to export the token instead (which is a bytes
# object) and pass it to Credentials() whenever we need it.
g.client_token = ctx.delegated_creds.export()
# TODO: don't pass client_princ to f anymore since it's stored in flask.g
resp = make_response(f(client_princ, *args, **kwargs))

@ -4,6 +4,7 @@ import pwd
from typing import Union, Dict, List
from flask import g
import gssapi
import ldap3
from zope import component
from zope.interface import implementer
@ -34,11 +35,12 @@ class LDAPService:
if 'ldap_conn' in g:
return g.ldap_conn
kwargs = {'auto_bind': True, 'raise_exceptions': True}
if 'client_creds' in g:
if 'client_token' in g:
kwargs['authentication'] = ldap3.SASL
kwargs['sasl_mechanism'] = ldap3.KERBEROS
creds = gssapi.Credentials(token=g.client_token)
# see https://github.com/cannatag/ldap3/blob/master/ldap3/protocol/sasl/kerberos.py
kwargs['sasl_credentials'] = (None, None, g.client_creds)
kwargs['sasl_credentials'] = (None, None, creds)
conn = ldap3.Connection(self.ldap_server_url, **kwargs)
# cache the connection for a single request
g.ldap_conn = conn

@ -11,13 +11,14 @@ import time
from unittest.mock import patch, Mock
import flask
import gssapi
import ldap3
import pytest
import requests
import socket
from zope import component
from .utils import gssapi_creds_ctx, ccache_cleanup # noqa: F401
from .utils import gssapi_token_ctx, ccache_cleanup # noqa: F401
from ceo_common.interfaces import IConfig, IKerberosService, ILDAPService, \
IFileService, IMailmanService, IHTTPClient, IUWLDAPService, IMailService
from ceo_common.model import Config, HTTPClient
@ -99,13 +100,13 @@ def g_admin_ctx(app):
"""
@contextlib.contextmanager
def wrapper():
with gssapi_creds_ctx('ceod/admin') as creds, app.app_context():
with gssapi_token_ctx('ceod/admin') as token, app.app_context():
try:
flask.g.auth_user = 'ceod/admin'
flask.g.client_creds = creds
flask.g.client_token = token
yield
finally:
flask.g.pop('client_creds')
flask.g.pop('client_token')
flask.g.pop('auth_user')
return wrapper
@ -117,13 +118,13 @@ def g_syscom(app):
Use this fixture if you need syscom credentials for an HTTP request
to a different process.
"""
with gssapi_creds_ctx('ctdalek') as creds, app.app_context():
with gssapi_token_ctx('ctdalek') as token, app.app_context():
try:
flask.g.sasl_user = 'ctdalek'
flask.g.client_creds = creds
flask.g.client_token = token
yield
finally:
flask.g.pop('client_creds')
flask.g.pop('client_token')
flask.g.pop('sasl_user')
@ -135,7 +136,8 @@ def ldap_conn(cfg) -> ldap3.Connection:
server_url = cfg.get('ldap_server_url')
# sanity check
assert server_url == cfg.get('uwldap_server_url')
with gssapi_creds_ctx('ceod/admin') as creds:
with gssapi_token_ctx('ceod/admin') as token:
creds = gssapi.Credentials(token=token)
conn = ldap3.Connection(
server_url, auto_bind=True, raise_exceptions=True,
authentication=ldap3.SASL, sasl_mechanism=ldap3.KERBEROS,
@ -369,7 +371,7 @@ def app_process(cfg, app, http_client):
try:
# Currently the HTTPClient uses SPNEGO for all requests,
# even GETs
with gssapi_creds_ctx('ctdalek'):
with gssapi_token_ctx('ctdalek'):
for i in range(5):
try:
http_client.get(hostname, '/ping', delegate=False)

@ -2,7 +2,7 @@ import os
import pytest
from .utils import gssapi_creds_ctx
from .utils import gssapi_token_ctx
@pytest.fixture(scope='module')
@ -14,5 +14,5 @@ def cli_setup(app_process):
# messy because they would be sharing the same environment variables,
# Kerberos cache, and registered utilities (via zope). So we're just
# going to start the app in a child process intead.
with gssapi_creds_ctx('ctdalek'):
with gssapi_token_ctx('ctdalek'):
yield

@ -8,7 +8,7 @@ import pytest
from requests import Request
from requests_gssapi import HTTPSPNEGOAuth
from .utils import gssapi_creds_ctx
from .utils import gssapi_token_ctx
__all__ = ['client']
@ -30,11 +30,11 @@ class CeodTestClient:
def get_auth(self, principal: str, delegate: bool):
"""Acquire a HTTPSPNEGOAuth instance for the principal."""
with gssapi_creds_ctx(principal) as creds:
with gssapi_token_ctx(principal) as token:
return HTTPSPNEGOAuth(
opportunistic_auth=True,
target_name=self.target_name,
creds=creds,
creds=gssapi.Credentials(token=token),
delegate=delegate,
)

@ -13,7 +13,7 @@ _cache = {}
@contextlib.contextmanager
def gssapi_creds_ctx(principal: str):
def gssapi_token_ctx(principal: str):
"""
Temporarily set KRB5CCNAME to a ccache storing credentials
for the specified user, and yield the GSSAPI credentials.
@ -35,7 +35,7 @@ def gssapi_creds_ctx(principal: str):
else:
creds, f = _cache[principal]
os.environ['KRB5CCNAME'] = 'FILE:' + f.name
yield creds
yield creds.export()
finally:
os.environ['KRB5CCNAME'] = old_krb5ccname

Loading…
Cancel
Save