add simple authz tests
This commit is contained in:
parent
26fd8f6f68
commit
490abb302c
|
@ -1,4 +1,5 @@
|
|||
__pycache__/
|
||||
*.pyc
|
||||
/venv/
|
||||
.vscode/
|
||||
/cred
|
||||
|
|
|
@ -93,7 +93,8 @@ def get_fwd_tgt(server: str, cache_name: Union[str, None] = None) -> bytes:
|
|||
e.g. 'ceod/phosphoric-acid.csclub.uwaterloo.ca'.
|
||||
|
||||
If `cache_name` is None, the default cache will be used; otherwise,
|
||||
the cache with that name will be used.
|
||||
the cache with that name will be used. Must have the format
|
||||
'TYPE:residual'.
|
||||
"""
|
||||
if cache_name is None:
|
||||
cache_function = get_krb5_cc_default
|
||||
|
|
|
@ -80,7 +80,7 @@ def test_api_next_uid(cfg, client, create_user_result):
|
|||
|
||||
|
||||
def test_api_get_user(cfg, client, create_user_result):
|
||||
old_data = create_user_result
|
||||
old_data = create_user_result.copy()
|
||||
uid = old_data['uid']
|
||||
del old_data['password']
|
||||
|
||||
|
@ -145,7 +145,7 @@ def test_api_patch_user(client, create_user_result):
|
|||
|
||||
|
||||
def test_api_renew_user(cfg, client, create_user_result, ldap_conn):
|
||||
data = create_user_result
|
||||
data = create_user_result.copy()
|
||||
uid = data['uid']
|
||||
old_terms = data['terms']
|
||||
old_non_member_terms = data.get('non_member_terms', [])
|
||||
|
@ -188,3 +188,19 @@ def test_api_reset_password(client, create_user_result):
|
|||
status, data = client.post(f'/api/members/{uid}/pwreset')
|
||||
assert status == 200
|
||||
assert data['password'] == 'krb5'
|
||||
|
||||
|
||||
def test_authz_check(client, create_user_result):
|
||||
# non-staff members may not create users
|
||||
status, data = client.post('/api/members', json={
|
||||
'uid': 'test_1', 'cn': 'Test One', 'terms': ['s2021'],
|
||||
}, principal='regular1')
|
||||
assert status == 403
|
||||
|
||||
# non-syscom members may not see forwarding addresses
|
||||
old_data = create_user_result.copy()
|
||||
uid = old_data['uid']
|
||||
del old_data['password']
|
||||
del old_data['forwarding_addresses']
|
||||
_, data = client.get(f'/api/members/{uid}', principal='regular1')
|
||||
assert data == old_data
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from base64 import b64encode
|
||||
import contextlib
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import subprocess
|
||||
|
@ -29,18 +31,28 @@ class CeodTestClient:
|
|||
self.syscom_principal = 'ctdalek'
|
||||
# this is only used for the HTTPSNEGOAuth
|
||||
self.base_url = f'http://{socket.getfqdn()}'
|
||||
# keep a list of all of the principals for which we acquired a TGT
|
||||
self.principals = []
|
||||
# for each principal for which we acquired a TGT, map their
|
||||
# username to a file (ccache) storing their TGT
|
||||
self.principal_ccaches = {}
|
||||
# this is where we'll store the credentials for each principal
|
||||
self.ccache = 'DIR:' + cache_dir
|
||||
self.cache_dir = cache_dir
|
||||
|
||||
@contextlib.contextmanager
|
||||
def krb5ccname_env(self, principal):
|
||||
"""Temporarily change KRB5CCNAME to the ccache of the principal."""
|
||||
old_krb5ccname = os.environ['KRB5CCNAME']
|
||||
os.environ['KRB5CCNAME'] = self.principal_ccaches[principal]
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.environ['KRB5CCNAME'] = old_krb5ccname
|
||||
|
||||
def get_auth(self, principal):
|
||||
"""Acquire a HTTPSPNEGOAuth instance for the principal."""
|
||||
name = gssapi.Name(principal)
|
||||
creds = gssapi.Credentials(
|
||||
name=name,
|
||||
usage='initiate',
|
||||
store={'ccache': self.ccache},
|
||||
)
|
||||
# the 'store' arg doesn't seem to work for DIR ccaches
|
||||
with self.krb5ccname_env(principal):
|
||||
creds = gssapi.Credentials(name=name, usage='initiate')
|
||||
auth = HTTPSPNEGOAuth(
|
||||
opportunistic_auth=True,
|
||||
target_name='ceod',
|
||||
|
@ -48,23 +60,30 @@ class CeodTestClient:
|
|||
)
|
||||
return auth
|
||||
|
||||
def get_headers(self, principal):
|
||||
if principal not in self.principals:
|
||||
# Acquire the initial TGT
|
||||
def kinit(self, principal):
|
||||
"""Acquire an initial TGT for the principal."""
|
||||
# For some reason, kinit with the '-c' option deletes the other
|
||||
# credentials in the cache collection, so we need to override the
|
||||
# env variable
|
||||
subprocess.run(
|
||||
['kinit', '-c', self.ccache, principal],
|
||||
text=True, input='krb5',
|
||||
check=True, stdout=subprocess.DEVNULL)
|
||||
self.principals.append(principal)
|
||||
['kinit', principal],
|
||||
text=True, input='krb5', check=True, stdout=subprocess.DEVNULL,
|
||||
env={'KRB5CCNAME': self.principal_ccaches[principal]})
|
||||
|
||||
def get_headers(self, principal):
|
||||
if principal not in self.principal_ccaches:
|
||||
_, filename = tempfile.mkstemp(dir=self.cache_dir)
|
||||
self.principal_ccaches[principal] = filename
|
||||
self.kinit(principal)
|
||||
# Get the Authorization header (SPNEGO).
|
||||
# The method doesn't matter here because we just need to extract
|
||||
# the header using req.prepare().
|
||||
req = Request('GET', self.base_url, auth=self.get_auth(principal))
|
||||
headers = list(req.prepare().headers.items())
|
||||
# Get the X-KRB5-CRED header (forwarded TGT).
|
||||
cred = b64encode(
|
||||
get_fwd_tgt('ceod/' + socket.getfqdn(), self.ccache)
|
||||
).decode()
|
||||
cred = b64encode(get_fwd_tgt(
|
||||
'ceod/' + socket.getfqdn(), self.principal_ccaches[principal]
|
||||
)).decode()
|
||||
headers.append(('X-KRB5-CRED', cred))
|
||||
return headers
|
||||
|
||||
|
|
Loading…
Reference in New Issue