Merge branch 'v1' into db-api
This commit is contained in:
commit
01b4412b42
|
@ -30,10 +30,10 @@ class HTTPClient:
|
||||||
'opportunistic_auth': True,
|
'opportunistic_auth': True,
|
||||||
'target_name': gssapi.Name('ceod/' + host),
|
'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
|
# This is reached when we are the server and the client has forwarded
|
||||||
# their credentials to us.
|
# their credentials to us.
|
||||||
spnego_kwargs['creds'] = flask.g.client_creds
|
spnego_kwargs['creds'] = gssapi.Credentials(token=flask.g.client_token)
|
||||||
if delegate:
|
if delegate:
|
||||||
# This is reached when we are the client and we want to forward our
|
# This is reached when we are the client and we want to forward our
|
||||||
# credentials to the server.
|
# credentials to the server.
|
||||||
|
|
|
@ -52,7 +52,11 @@ def requires_authentication(f):
|
||||||
|
|
||||||
# Store the delegated credentials, if they were given
|
# Store the delegated credentials, if they were given
|
||||||
if ctx.actual_flags & RequirementFlag.delegate_to_peer:
|
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
|
# TODO: don't pass client_princ to f anymore since it's stored in flask.g
|
||||||
resp = make_response(f(client_princ, *args, **kwargs))
|
resp = make_response(f(client_princ, *args, **kwargs))
|
||||||
|
|
|
@ -4,6 +4,7 @@ import pwd
|
||||||
from typing import Union, Dict, List
|
from typing import Union, Dict, List
|
||||||
|
|
||||||
from flask import g
|
from flask import g
|
||||||
|
import gssapi
|
||||||
import ldap3
|
import ldap3
|
||||||
from zope import component
|
from zope import component
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
@ -34,11 +35,12 @@ class LDAPService:
|
||||||
if 'ldap_conn' in g:
|
if 'ldap_conn' in g:
|
||||||
return g.ldap_conn
|
return g.ldap_conn
|
||||||
kwargs = {'auto_bind': True, 'raise_exceptions': True}
|
kwargs = {'auto_bind': True, 'raise_exceptions': True}
|
||||||
if 'client_creds' in g:
|
if 'client_token' in g:
|
||||||
kwargs['authentication'] = ldap3.SASL
|
kwargs['authentication'] = ldap3.SASL
|
||||||
kwargs['sasl_mechanism'] = ldap3.KERBEROS
|
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
|
# 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)
|
conn = ldap3.Connection(self.ldap_server_url, **kwargs)
|
||||||
# cache the connection for a single request
|
# cache the connection for a single request
|
||||||
g.ldap_conn = conn
|
g.ldap_conn = conn
|
||||||
|
|
|
@ -12,13 +12,14 @@ import time
|
||||||
from unittest.mock import patch, Mock
|
from unittest.mock import patch, Mock
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
import gssapi
|
||||||
import ldap3
|
import ldap3
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
import socket
|
import socket
|
||||||
from zope import component
|
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, \
|
from ceo_common.interfaces import IConfig, IKerberosService, ILDAPService, \
|
||||||
IFileService, IMailmanService, IHTTPClient, IUWLDAPService, IMailService, \
|
IFileService, IMailmanService, IHTTPClient, IUWLDAPService, IMailService, \
|
||||||
IDatabaseService
|
IDatabaseService
|
||||||
|
@ -102,13 +103,13 @@ def g_admin_ctx(app):
|
||||||
"""
|
"""
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def wrapper():
|
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:
|
try:
|
||||||
flask.g.auth_user = 'ceod/admin'
|
flask.g.auth_user = 'ceod/admin'
|
||||||
flask.g.client_creds = creds
|
flask.g.client_token = token
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
flask.g.pop('client_creds')
|
flask.g.pop('client_token')
|
||||||
flask.g.pop('auth_user')
|
flask.g.pop('auth_user')
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@ -120,13 +121,13 @@ def g_syscom(app):
|
||||||
Use this fixture if you need syscom credentials for an HTTP request
|
Use this fixture if you need syscom credentials for an HTTP request
|
||||||
to a different process.
|
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:
|
try:
|
||||||
flask.g.sasl_user = 'ctdalek'
|
flask.g.sasl_user = 'ctdalek'
|
||||||
flask.g.client_creds = creds
|
flask.g.client_token = token
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
flask.g.pop('client_creds')
|
flask.g.pop('client_token')
|
||||||
flask.g.pop('sasl_user')
|
flask.g.pop('sasl_user')
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,7 +139,8 @@ def ldap_conn(cfg) -> ldap3.Connection:
|
||||||
server_url = cfg.get('ldap_server_url')
|
server_url = cfg.get('ldap_server_url')
|
||||||
# sanity check
|
# sanity check
|
||||||
assert server_url == cfg.get('uwldap_server_url')
|
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(
|
conn = ldap3.Connection(
|
||||||
server_url, auto_bind=True, raise_exceptions=True,
|
server_url, auto_bind=True, raise_exceptions=True,
|
||||||
authentication=ldap3.SASL, sasl_mechanism=ldap3.KERBEROS,
|
authentication=ldap3.SASL, sasl_mechanism=ldap3.KERBEROS,
|
||||||
|
@ -392,7 +394,7 @@ def app_process(cfg, app, http_client):
|
||||||
try:
|
try:
|
||||||
# Currently the HTTPClient uses SPNEGO for all requests,
|
# Currently the HTTPClient uses SPNEGO for all requests,
|
||||||
# even GETs
|
# even GETs
|
||||||
with gssapi_creds_ctx('ctdalek'):
|
with gssapi_token_ctx('ctdalek'):
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
try:
|
try:
|
||||||
http_client.get(hostname, '/ping', delegate=False)
|
http_client.get(hostname, '/ping', delegate=False)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from .utils import gssapi_creds_ctx
|
from .utils import gssapi_token_ctx
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@pytest.fixture(scope='module')
|
||||||
|
@ -14,5 +14,5 @@ def cli_setup(app_process):
|
||||||
# messy because they would be sharing the same environment variables,
|
# messy because they would be sharing the same environment variables,
|
||||||
# Kerberos cache, and registered utilities (via zope). So we're just
|
# Kerberos cache, and registered utilities (via zope). So we're just
|
||||||
# going to start the app in a child process intead.
|
# going to start the app in a child process intead.
|
||||||
with gssapi_creds_ctx('ctdalek'):
|
with gssapi_token_ctx('ctdalek'):
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -8,7 +8,7 @@ import pytest
|
||||||
from requests import Request
|
from requests import Request
|
||||||
from requests_gssapi import HTTPSPNEGOAuth
|
from requests_gssapi import HTTPSPNEGOAuth
|
||||||
|
|
||||||
from .utils import gssapi_creds_ctx
|
from .utils import gssapi_token_ctx
|
||||||
|
|
||||||
__all__ = ['client']
|
__all__ = ['client']
|
||||||
|
|
||||||
|
@ -30,11 +30,11 @@ class CeodTestClient:
|
||||||
|
|
||||||
def get_auth(self, principal: str, delegate: bool):
|
def get_auth(self, principal: str, delegate: bool):
|
||||||
"""Acquire a HTTPSPNEGOAuth instance for the principal."""
|
"""Acquire a HTTPSPNEGOAuth instance for the principal."""
|
||||||
with gssapi_creds_ctx(principal) as creds:
|
with gssapi_token_ctx(principal) as token:
|
||||||
return HTTPSPNEGOAuth(
|
return HTTPSPNEGOAuth(
|
||||||
opportunistic_auth=True,
|
opportunistic_auth=True,
|
||||||
target_name=self.target_name,
|
target_name=self.target_name,
|
||||||
creds=creds,
|
creds=gssapi.Credentials(token=token),
|
||||||
delegate=delegate,
|
delegate=delegate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ _cache = {}
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def gssapi_creds_ctx(principal: str):
|
def gssapi_token_ctx(principal: str):
|
||||||
"""
|
"""
|
||||||
Temporarily set KRB5CCNAME to a ccache storing credentials
|
Temporarily set KRB5CCNAME to a ccache storing credentials
|
||||||
for the specified user, and yield the GSSAPI credentials.
|
for the specified user, and yield the GSSAPI credentials.
|
||||||
|
@ -35,7 +35,7 @@ def gssapi_creds_ctx(principal: str):
|
||||||
else:
|
else:
|
||||||
creds, f = _cache[principal]
|
creds, f = _cache[principal]
|
||||||
os.environ['KRB5CCNAME'] = 'FILE:' + f.name
|
os.environ['KRB5CCNAME'] = 'FILE:' + f.name
|
||||||
yield creds
|
yield creds.export()
|
||||||
finally:
|
finally:
|
||||||
os.environ['KRB5CCNAME'] = old_krb5ccname
|
os.environ['KRB5CCNAME'] = old_krb5ccname
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue