Skip to content

Commit

Permalink
Merge pull request #13 from tangkong/enh_composite_auth
Browse files Browse the repository at this point in the history
ENH: add CompositeAuth request auth scheme
  • Loading branch information
ZLLentz authored Sep 22, 2023
2 parents 81c6603 + 712526c commit 21e9880
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 2 deletions.
1 change: 1 addition & 0 deletions conda-recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ requirements:
run:
- python
- pykerberos
- requests

test:
imports:
Expand Down
3 changes: 2 additions & 1 deletion krtc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .cmpauth import CompositeAuth
from .krtc import KerberosTicket
from .version import __version__ # noqa: F401

__all__ = ["KerberosTicket"]
__all__ = ["KerberosTicket", "CompositeAuth"]
120 changes: 120 additions & 0 deletions krtc/cmpauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import argparse
import getpass
import logging
import os
import re
from urllib.parse import urlparse

import requests
from requests.auth import AuthBase, HTTPBasicAuth

from .krtc import KerberosTicket

logger = logging.getLogger(__name__)


class CompositeAuth(AuthBase):
"""
Examine the environment and try to use the 'best' possible authentication
scheme when talking to the LCLS logbook.
1. Check to see if we are running in the context of an ARP job
and have a bearer token; if so, use that.
2. See if we have a Kerberos token and if so, use that.
Note, we do not validate if the token is valid;
we just check for presence/absence.
3. If an operator userid and password are specified; use that.
4. Finally, if none of these are specified; try to make the call as is.
Usage:
r = requests.post(
ws_url,
params={"run_num": run},
json=runtable_data,
auth=CompositeAuth(operatorid=experiment[:3]+'opr',
password=answer[:-1]))
You can use any variant of the
https://psww.../<ws/ws-auth/ws-kerb/ws-jwt>/lgbk URL
and the URL will be adjusted according
to the auth scheme that is finally used.
"""
def __init__(self, operatorid=None, password=None):
self.operatorid = operatorid
self.password = password

def __call__(self, r):
uu = "^https://pswww.slac.stanford.edu/ws[^/]*/lgbk/(.*)$"
theregex = re.compile(uu)
if not theregex.match(r.url):
raise Exception("For now, this only applies to eLog URL's")
# 1. Check to see if we are running in the context of an ARP job
# and have a bearer token; if so, use that.
if "Authorization" in os.environ and "Bearer " in os.environ["Authorization"]:
r.url = "https://pswww.slac.stanford.edu/ws-jwt/lgbk/" \
+ theregex.match(r.url)[1]
logger.debug(
"Inside an ARP job, using the JWT endpoint %s",
r.url)
r.headers["Authorization"] = os.environ["Authorization"]
return r
# See if we have a Kerberos token and if so, use that.
# Note, we do not validate if the token is valid;
# we just check for presence/absence.
# Also skip if we logged in as an operator
loggedinuser = getpass.getuser()
amoperator = len(loggedinuser) == 6 and loggedinuser.endswith("opr")
if not amoperator \
and "KRB5CCNAME" in os.environ \
and os.path.exists(urlparse(os.environ["KRB5CCNAME"]).path):
r.url = "https://pswww.slac.stanford.edu/ws-kerb/lgbk/" \
+ theregex.match(r.url)[1]
logger.debug(
"Found a Kerberos ticket;"
"using the kerberos endpoint %s",
r.url)
krbheaders = KerberosTicket(
"HTTP@" + urlparse(r.url).hostname).getAuthHeaders()
r.headers.update(krbheaders)
return r
# If an operator userid and password are specified; use that.
if self.operatorid and self.password:
r.url = "https://pswww.slac.stanford.edu/ws-auth/lgbk/" \
+ theregex.match(r.url)[1]
logger.debug(
"Using the operator id %s with the url %s",
self.operatorid, r.url)
basic = HTTPBasicAuth(self.operatorid, self.password)
return basic.__call__(r)

logger.debug("No authentication found; invoking as is %s", r.url)
return r


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-v", "--verbose",
action='store_true', help="Turn on verbose logging")
parser.add_argument(
"-u", "--userid",
action='store', help="Optional operator id")
parser.add_argument(
"-p", "--password",
action='store', help="Optional operator password")
parser.add_argument("url", help="A eLog URL to test")

args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)

if args.userid and args.password:
theauth = CompositeAuth(
operatorid=args.userid,
password=args.password
)
else:
theauth = CompositeAuth()

resp = requests.get(
args.url,
auth=theauth
)
resp.raise_for_status()
print(resp.json())
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ file = [ "requirements.txt",]
[tool.setuptools.dynamic.optional-dependencies.test]
file = "dev-requirements.txt"

[tool.setuptools.dynamic.optional-dependencies.docs]
[tool.setuptools.dynamic.optional-dependencies.doc]
file = "docs-requirements.txt"
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pykerberos
requests

0 comments on commit 21e9880

Please sign in to comment.