Merge branch 'apache-auth' into operation_standardisation
This commit is contained in:
@@ -3,7 +3,6 @@ import ipaddress
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import re
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from typing import List
|
||||
@@ -35,10 +34,6 @@ BATON_REQUEST_TIMEOUT_SECONDS = 30
|
||||
STAFF_GROUP = "unx-MXgroup"
|
||||
SUPER_USERS = ["e10019", "e11206", "e18147"]
|
||||
|
||||
APACHE_ACCESS_LOG = "/var/log/httpd/daq-access.log"
|
||||
# Common Log Format: IP - username [timestamp] "request" status bytes ...
|
||||
_LOG_PATTERN = re.compile(r'^\S+ \S+ (\S+) \[.*?\] ".*?" (\d+)')
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
|
||||
class TokenData(BaseModel):
|
||||
@@ -55,35 +50,6 @@ def create_access_token(token: TokenData):
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
def authenticate_from_apache_log() -> str:
|
||||
"""
|
||||
Read the Apache access log and return the username from the most recent
|
||||
200 response. Apache and the DAQ server are co-located on the same machine.
|
||||
"""
|
||||
try:
|
||||
with open(APACHE_ACCESS_LOG, 'r') as f:
|
||||
lines = f.readlines()
|
||||
except OSError as e:
|
||||
raise AuthenticationException(
|
||||
message=f"Cannot read Apache access log: {e}",
|
||||
status_code=401,
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
code=AuthErrorCode.INVALID_TOKEN,
|
||||
) from e
|
||||
|
||||
for line in reversed(lines):
|
||||
m = _LOG_PATTERN.match(line)
|
||||
if m and m.group(2) == '200' and m.group(1) != '-':
|
||||
return m.group(1)
|
||||
|
||||
raise AuthenticationException(
|
||||
message="No authenticated user found in Apache access log",
|
||||
status_code=401,
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
code=AuthErrorCode.INVALID_TOKEN,
|
||||
)
|
||||
|
||||
|
||||
def _is_loopback(host: str | None) -> bool:
|
||||
if host is None:
|
||||
return False
|
||||
|
||||
@@ -260,21 +260,18 @@ async def login(request: Request, form_data: OAuth2PasswordRequestForm = Depends
|
||||
"""
|
||||
Authenticate a user and return an access token.
|
||||
|
||||
When the request carries an X-Remote-User header (set by the Apache Kerberos
|
||||
proxy), the username is taken from that header and the proxy origin is verified.
|
||||
Otherwise the username from the form data is used (local / dev access).
|
||||
The request must carry an X-Remote-User header set by the Apache Kerberos
|
||||
proxy. The client obtains a token by authenticating via Kerberos (NEGOTIATE)
|
||||
against the Apache proxy, which forwards the request with X-Remote-User set.
|
||||
|
||||
Args:
|
||||
request: The incoming HTTP request (used to inspect headers and client IP).
|
||||
form_data: OAuth2 password request form containing username and password.
|
||||
form_data: OAuth2 password request form (unused, required by OAuth2 spec).
|
||||
|
||||
Returns:
|
||||
A dictionary containing the access token and token type.
|
||||
"""
|
||||
if request.headers.get("X-Remote-User"):
|
||||
username = auth.authenticate_from_proxy_header(request)
|
||||
else:
|
||||
username = auth.authenticate_from_apache_log()
|
||||
username = auth.authenticate_from_proxy_header(request)
|
||||
data = await run_in_threadpool(auth.authenticate_user, cfg, username)
|
||||
return {"access_token": data, "token_type": "bearer"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user