Skip to content

Commit

Permalink
fix config, add check extra hash
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenifer Tabita Ciuciu-Kiss committed Oct 20, 2024
1 parent 046a2dd commit 5da7694
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 75 deletions.
22 changes: 7 additions & 15 deletions ontologytimemachine/custom_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
get_response_from_request,
do_block_CONNECT_request,
is_archivo_ontology_request,
evaluate_configuration,
)
from ontologytimemachine.utils.config import Config, HttpsInterception, parse_arguments
from http.client import responses
import proxy
import sys
import logging
from ontologytimemachine.utils.config import HttpsInterception
from ontologytimemachine.utils.config import HttpsInterception, ClientConfigViaProxyAuth


IP = "0.0.0.0"
Expand All @@ -36,28 +37,18 @@ def __init__(self, *args, **kwargs):
logger.info("Init")
super().__init__(*args, **kwargs)
self.config = config
self.current_config = None

def before_upstream_connection(self, request: HttpParser) -> HttpParser | None:
# self.client.config = None
logger.info("Before upstream connection hook")
logger.info(
f"Request method: {request.method} - Request host: {request.host} - Request path: {request.path} - Request headers: {request.headers}"
)
wrapped_request = HttpRequestWrapper(request)

authentication = wrapped_request.get_authentication_from_request()
if authentication:
username, password = authentication.split(":")
logger.info(f"Username: {username} - password: {password}")
if self.config.username == username and self.config.password == password:
logger.info("Successful authentication")
else:
logger.info("Authentication was not successful")
self.queue_response(mock_response_500)
return None
else:
logger.info("Authentication required.")
self.queue_response(mock_response_500)
return None
# if self.config.clientConfigViaProxyAuth == ClientConfigViaProxyAuth.REQUIRED:
# self.client.config = evaluate_configuration(wrapped_request, self.config)

if wrapped_request.is_connect_request():
logger.info(
Expand All @@ -77,6 +68,7 @@ def before_upstream_connection(self, request: HttpParser) -> HttpParser | None:
response = get_response_from_request(wrapped_request, self.config)
if response:
self.queue_response(response)
self.current_config = None
return None

return request
Expand Down
10 changes: 8 additions & 2 deletions ontologytimemachine/proxy_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,14 @@ def get_request_url_host_path(self) -> Tuple[str, str, str]:
return url, host, path

def get_authentication_from_request(self) -> str:
if b"authorization" in self.request.headers.keys():
auth_header = self.request.headers[b"authorization"]
is_auth = False
# if b"authorization" in self.request.headers.keys():
# auth_header = self.request.headers[b"authorization"]
# is_auth = True
if b"proxy-authorization" in self.request.headers.keys():
auth_header = self.request.headers[b"proxy-authorization"]
is_auth = True
if is_auth:
auth_header = auth_header[1]
auth_type, encoded_credentials = auth_header.split()
auth_type = auth_type.decode("utf-8")
Expand Down
112 changes: 63 additions & 49 deletions ontologytimemachine/utils/config.py
Original file line number Diff line number Diff line change
@@ -1,157 +1,174 @@
import argparse
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, Any
from typing import Dict, Any, Type, TypeVar


class LogLevel(Enum):
class EnumValuePrint(
Enum
): # redefine how the enum is printed such that it will show up properly the cmd help message (choices)
def __str__(self):
return self.value


class LogLevel(EnumValuePrint):
DEBUG = "debug"
INFO = "info"
WARNING = "warning"
ERROR = "error"


class OntoFormat(Enum):
class OntoFormat(EnumValuePrint):
TURTLE = "turtle"
NTRIPLES = "ntriples"
RDFXML = "rdfxml"
HTMLDOCU = "htmldocu"


class OntoPrecedence(Enum):
class OntoPrecedence(EnumValuePrint):
DEFAULT = "default"
ENFORCED_PRIORITY = "enforcedPriority"
ALWAYS = "always"


class OntoVersion(Enum):
class OntoVersion(EnumValuePrint):
ORIGINAL = "original"
ORIGINAL_FAILOVER_LIVE_LATEST = "originalFailoverLiveLatest"
LATEST_ARCHIVED = "latestArchived"
TIMESTAMP_ARCHIVED = "timestampArchived"
# DEPENDENCY_MANIFEST = "dependencyManifest"
DEPENDENCY_MANIFEST = "dependencyManifest"


class HttpsInterception(Enum):
class HttpsInterception(EnumValuePrint):
NONE = "none"
ALL = "all"
BLOCK = "block"
ARCHIVO = "archivo"


class ClientConfigViaProxyAuth(EnumValuePrint):
IGNORE = "ignore"
REQUIRED = "required"
OPTIONAL = "optional"


@dataclass
class OntoFormatConfig:
format: OntoFormat = OntoFormat.TURTLE
format: OntoFormat = OntoFormat.NTRIPLES
precedence: OntoPrecedence = OntoPrecedence.ENFORCED_PRIORITY
patchAcceptUpstream: bool = False


@dataclass
class Config:
logLevel: LogLevel = LogLevel.INFO
ontoFormat: OntoFormatConfig = field(default_factory=OntoFormatConfig)
ontoFormatConf: OntoFormatConfig = field(default_factory=OntoFormatConfig)
ontoVersion: OntoVersion = OntoVersion.ORIGINAL_FAILOVER_LIVE_LATEST
restrictedAccess: bool = False
clientConfigViaProxyAuth: ClientConfigViaProxyAuth = (
ClientConfigViaProxyAuth.REQUIRED
)
httpsInterception: HttpsInterception = HttpsInterception.ALL
disableRemovingRedirects: bool = False
timestamp: str = ""
username: str = ""
password: str = ""
# manifest: Dict[str, Any] = None


def enum_parser(enum_class, value):
# Define a TypeVar for the enum class
E = TypeVar("E", bound=Enum)


def enum_parser(enum_class: Type[E], value: str) -> E:
value_lower = value.lower()
try:
return next(e for e in enum_class if e.value.lower() == value_lower)
except StopIteration:
except StopIteration as exc:
valid_options = ", ".join([e.value for e in enum_class])
raise ValueError(
raise argparse.ArgumentTypeError(
f"Invalid value '{value}'. Available options are: {valid_options}"
)
) from exc


def parse_arguments() -> Config:
def parse_arguments(config_str: str = "") -> Config:
default_cfg: Config = Config()
parser = argparse.ArgumentParser(description="Process ontology format and version.")

# Defining ontoFormat argument with nested options
parser.add_argument(
"--ontoFormat",
type=lambda s: enum_parser(OntoFormat, s),
default=OntoFormat.TURTLE.value,
default=default_cfg.ontoFormatConf.format,
choices=list(OntoFormat),
help="Format of the ontology: turtle, ntriples, rdfxml, htmldocu",
)

parser.add_argument(
"--ontoPrecedence",
type=lambda s: enum_parser(OntoPrecedence, s),
default=OntoPrecedence.ENFORCED_PRIORITY,
default=default_cfg.ontoFormatConf.precedence,
choices=list(OntoPrecedence),
help="Precedence of the ontology: default, enforcedPriority, always",
)

parser.add_argument(
"--patchAcceptUpstream",
type=bool,
default=False,
default=default_cfg.ontoFormatConf.patchAcceptUpstream,
help="Defines if the Accept Header is patched upstream in original mode.",
)

# Defining ontoVersion argument
parser.add_argument(
"--ontoVersion",
type=lambda s: enum_parser(OntoVersion, s),
default=OntoVersion.ORIGINAL_FAILOVER_LIVE_LATEST.value,
default=default_cfg.ontoVersion,
choices=list(OntoVersion),
help="Version of the ontology: original, originalFailoverLive, originalFailoverArchivoMonitor, latestArchive, timestampArchive, dependencyManifest",
)

# Enable/disable mode to only proxy requests to ontologies
parser.add_argument(
"--restrictedAccess",
type=bool,
default=False,
default=default_cfg.restrictedAccess,
help="Enable/disable mode to only proxy requests to ontologies stored in Archivo.",
)

# Enable HTTPS interception for specific domains
parser.add_argument(
"--httpsInterception",
type=lambda s: enum_parser(HttpsInterception, s),
default=HttpsInterception.ALL,
default=default_cfg.httpsInterception,
choices=list(HttpsInterception),
help="Enable HTTPS interception for specific domains: none, archivo, all, listfilename.",
)

# Enable/disable inspecting or removing redirects
parser.add_argument(
"--disableRemovingRedirects",
type=bool,
default=False,
default=default_cfg.disableRemovingRedirects,
help="Enable/disable inspecting or removing redirects.",
)

parser.add_argument(
"--clientConfigViaProxyAuth",
type=lambda s: enum_parser(ClientConfigViaProxyAuth, s),
default=default_cfg.clientConfigViaProxyAuth,
choices=list(ClientConfigViaProxyAuth),
help="Define the config.",
)

# Log level
parser.add_argument(
"--logLevel",
type=lambda s: enum_parser(LogLevel, s),
default=LogLevel.INFO,
default=default_cfg.logLevel,
choices=list(LogLevel),
help="Level of the logging: debug, info, warning, error.",
)

# Adding username and password arguments
parser.add_argument(
"--username",
type=str,
default="admin",
help="Username for proxy authentication.",
)

parser.add_argument(
"--password",
type=str,
default="archivo",
help="Password for proxy authentication.",
)

args = parser.parse_args()
args = parser.parse_args(config_str)

# Check the value of --ontoVersion and prompt for additional arguments if needed
if args.ontoVersion == "timestampArchived":
Expand All @@ -172,24 +189,21 @@ def parse_arguments() -> Config:
# else:
# manifest = None

# Create the OntoFormatConfig using the parsed arguments
onto_format_config = OntoFormatConfig(
format=args.ontoFormat,
precedence=args.ontoPrecedence,
patchAcceptUpstream=args.patchAcceptUpstream,
)
# print the default configuration with all nested members
print(default_cfg) # TODO remove

# Initialize the Config class with parsed arguments
config = Config(
logLevel=args.logLevel,
ontoFormat=onto_format_config,
ontoFormatConf=OntoFormatConfig(
args.ontoFormat, args.ontoPrecedence, args.patchAcceptUpstream
),
ontoVersion=args.ontoVersion,
restrictedAccess=args.restrictedAccess,
httpsInterception=args.httpsInterception,
clientConfigViaProxyAuth=args.clientConfigViaProxyAuth,
disableRemovingRedirects=args.disableRemovingRedirects,
timestamp=args.timestamp if hasattr(args, "timestamp") else "",
username=args.username,
password=args.password,
)

return config
26 changes: 24 additions & 2 deletions ontologytimemachine/utils/proxy_logic.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import requests
from ontologytimemachine.utils.config import parse_arguments
from ontologytimemachine.proxy_wrapper import AbstractRequestWrapper
from ontologytimemachine.utils.config import Config, HttpsInterception
from ontologytimemachine.utils.utils import (
Expand Down Expand Up @@ -60,6 +61,21 @@ def get_response_from_request(wrapped_request, config):
return response


# curl -U "--ca-key-file+ca-key.pem+--ca-cert-file+ca-cert.pem+--ca-signing-key-file+ca-signing-key.pem+--hostname+0.0.0.0+--port+%24PORT+--plugins+ontologytimemachine.custom_proxy.OntologyTimeMachinePlugin+http%3A%2F%2Fweb.de%2F%3Ffoo%3Dbar%26bar%3Dfoo%23whateversssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss:pas" -kvvvx http://localhost:8899 https:///www.example.org
# decode auth username (is in www-form encoding not to beconfused with url encoding!)
# parameters parsed into config object
# configurations merged (cmdline startup config and auth config)
# apply for current request
def evaluate_configuration(wrapped_request, config):
authentication_str = wrapped_request.get_authentication_from_request()
print(authentication_str)
username, password = authentication_str.split(":")
logger.info(username)
config_list = username.split(" ")
config = parse_arguments(config_list)
return config


def is_archivo_ontology_request(wrapped_request):
"""Check if the requested ontology is in the archivo."""
logger.info("Check if the requested ontology is in archivo")
Expand Down Expand Up @@ -88,12 +104,16 @@ def is_archivo_ontology_request(wrapped_request):
path_parts = request_path.split("/")
new_path = "/".join(path_parts[:-1])

if (request_host, new_path) in ARCHIVO_PARSED_URLS:
if ((request_host, new_path) in ARCHIVO_PARSED_URLS) or (
(request_host, new_path + "/") in ARCHIVO_PARSED_URLS
):
logger.info(f"Requested URL: {request_host+request_path} is in Archivo")
return True

new_path = "/".join(path_parts[:-2])
if (request_host, new_path) in ARCHIVO_PARSED_URLS:
if ((request_host, new_path) in ARCHIVO_PARSED_URLS) or (
(request_host, new_path + "/") in ARCHIVO_PARSED_URLS
):
logger.info(f"Requested URL: {request_host+request_path} is in Archivo")
return True

Expand All @@ -118,6 +138,8 @@ def request_ontology(url, headers, disableRemovingRedirects=False, timeout=5):
def proxy_logic(wrapped_request, config):
logger.info("Proxy has to intervene")

print(wrapped_request)
print(config)
set_onto_format_headers(wrapped_request, config)

headers = wrapped_request.get_request_headers()
Expand Down
Loading

0 comments on commit 5da7694

Please sign in to comment.