Separate basic auth plugin outside of core server (#453)
* Separate basic auth plugin outside of core * Put basic auth plugin at top
This commit is contained in:
parent
0744cd8e7f
commit
137ce457bb
2
Makefile
2
Makefile
|
@ -110,7 +110,7 @@ lib-coverage:
|
|||
open htmlcov/index.html
|
||||
|
||||
lib-profile:
|
||||
sudo py-spy -F -f profile.svg -d 3600 proxy.py
|
||||
sudo py-spy record -o profile.svg -t -F -s -- python -m proxy
|
||||
|
||||
dashboard:
|
||||
pushd dashboard && npm run build && popd
|
||||
|
|
|
@ -86,6 +86,7 @@ PLUGIN_PAC_FILE = 'proxy.http.server.HttpWebServerPacFilePlugin'
|
|||
PLUGIN_DEVTOOLS_PROTOCOL = 'proxy.http.inspector.DevtoolsProtocolPlugin'
|
||||
PLUGIN_DASHBOARD = 'proxy.dashboard.dashboard.ProxyDashboard'
|
||||
PLUGIN_INSPECT_TRAFFIC = 'proxy.dashboard.inspect_traffic.InspectTrafficPlugin'
|
||||
PLUGIN_PROXY_AUTH = 'proxy.http.proxy.AuthPlugin'
|
||||
|
||||
PY2_DEPRECATION_MESSAGE = '''DEPRECATION: proxy.py no longer supports Python 2.7. Kindly upgrade to Python 3+. '
|
||||
'If for some reasons you cannot upgrade, use'
|
||||
|
|
|
@ -88,5 +88,6 @@ class TcpConnection(ABC):
|
|||
self.buffer.pop(0)
|
||||
else:
|
||||
self.buffer[0] = memoryview(mv[sent:])
|
||||
del mv
|
||||
logger.debug('flushed %d bytes to %s' % (sent, self.tag))
|
||||
return sent
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
"""
|
||||
from .plugin import HttpProxyBasePlugin
|
||||
from .server import HttpProxyPlugin
|
||||
from .auth import AuthPlugin
|
||||
|
||||
__all__ = [
|
||||
'HttpProxyBasePlugin',
|
||||
'HttpProxyPlugin',
|
||||
'AuthPlugin',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
proxy.py
|
||||
~~~~~~~~
|
||||
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
|
||||
Network monitoring, controls & Application development, testing, debugging.
|
||||
|
||||
:copyright: (c) 2013-present by Abhinav Singh and contributors.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
from ..exception import ProxyAuthenticationFailed
|
||||
from ...common.flag import flags
|
||||
from ...common.constants import DEFAULT_BASIC_AUTH
|
||||
from ...http.parser import HttpParser
|
||||
from ...http.proxy import HttpProxyBasePlugin
|
||||
|
||||
|
||||
flags.add_argument(
|
||||
'--basic-auth',
|
||||
type=str,
|
||||
default=DEFAULT_BASIC_AUTH,
|
||||
help='Default: No authentication. Specify colon separated user:password '
|
||||
'to enable basic authentication.')
|
||||
|
||||
|
||||
class AuthPlugin(HttpProxyBasePlugin):
|
||||
"""Performs proxy authentication."""
|
||||
|
||||
def before_upstream_connection(
|
||||
self, request: HttpParser) -> Optional[HttpParser]:
|
||||
if self.flags.auth_code:
|
||||
if b'proxy-authorization' not in request.headers:
|
||||
raise ProxyAuthenticationFailed()
|
||||
parts = request.headers[b'proxy-authorization'][1].split()
|
||||
if len(parts) != 2 and parts[0].lower(
|
||||
) != b'basic' and parts[1] != self.flags.auth_code:
|
||||
raise ProxyAuthenticationFailed()
|
||||
return request
|
||||
|
||||
def handle_client_request(
|
||||
self, request: HttpParser) -> Optional[HttpParser]:
|
||||
return request
|
||||
|
||||
def handle_upstream_chunk(self, chunk: memoryview) -> memoryview:
|
||||
return chunk
|
||||
|
||||
def on_upstream_connection_close(self) -> None:
|
||||
pass
|
|
@ -20,7 +20,7 @@ from typing import Optional, List, Union, Dict, cast, Any, Tuple
|
|||
|
||||
from .plugin import HttpProxyBasePlugin
|
||||
from ..plugin import HttpProtocolHandlerPlugin
|
||||
from ..exception import HttpProtocolException, ProxyConnectionFailed, ProxyAuthenticationFailed
|
||||
from ..exception import HttpProtocolException, ProxyConnectionFailed
|
||||
from ..codes import httpStatusCodes
|
||||
from ..parser import HttpParser, httpParserStates, httpParserTypes
|
||||
from ..methods import httpMethods
|
||||
|
@ -105,8 +105,7 @@ class HttpProxyPlugin(HttpProtocolHandlerPlugin):
|
|||
reason=b'Connection established'
|
||||
))
|
||||
|
||||
# Used to synchronize with other HttpProxyPlugin instances while
|
||||
# generating certificates
|
||||
# Used to synchronization during certificate generation.
|
||||
lock = threading.Lock()
|
||||
|
||||
def __init__(
|
||||
|
@ -322,8 +321,6 @@ class HttpProxyPlugin(HttpProtocolHandlerPlugin):
|
|||
|
||||
self.emit_request_complete()
|
||||
|
||||
self.authenticate()
|
||||
|
||||
# Note: can raise HttpRequestRejected exception
|
||||
# Invoke plugin.before_upstream_connection
|
||||
do_connect = True
|
||||
|
@ -533,15 +530,6 @@ class HttpProxyPlugin(HttpProtocolHandlerPlugin):
|
|||
logger.debug(
|
||||
'TLS interception using %s', generated_cert)
|
||||
|
||||
def authenticate(self) -> None:
|
||||
if self.flags.auth_code:
|
||||
if b'proxy-authorization' not in self.request.headers:
|
||||
raise ProxyAuthenticationFailed()
|
||||
parts = self.request.headers[b'proxy-authorization'][1].split()
|
||||
if len(parts) != 2 and parts[0].lower(
|
||||
) != b'basic' and parts[1] != self.flags.auth_code:
|
||||
raise ProxyAuthenticationFailed()
|
||||
|
||||
def connect_upstream(self) -> None:
|
||||
host, port = self.request.host, self.request.port
|
||||
if host and port:
|
||||
|
|
|
@ -32,7 +32,7 @@ from .common.version import __version__
|
|||
from .core.acceptor import AcceptorPool
|
||||
from .http.handler import HttpProtocolHandler
|
||||
from .common.flag import flags
|
||||
from .common.constants import COMMA, DEFAULT_BASIC_AUTH, DEFAULT_DATA_DIRECTORY_PATH
|
||||
from .common.constants import COMMA, DEFAULT_DATA_DIRECTORY_PATH, PLUGIN_PROXY_AUTH
|
||||
from .common.constants import DEFAULT_DEVTOOLS_WS_PATH, DEFAULT_DISABLE_HEADERS
|
||||
from .common.constants import DEFAULT_DISABLE_HTTP_PROXY, DEFAULT_NUM_WORKERS
|
||||
from .common.constants import DEFAULT_ENABLE_DASHBOARD, DEFAULT_ENABLE_DEVTOOLS
|
||||
|
@ -54,12 +54,6 @@ flags.add_argument(
|
|||
type=str,
|
||||
default=DEFAULT_PID_FILE,
|
||||
help='Default: None. Save parent process ID to a file.')
|
||||
flags.add_argument(
|
||||
'--basic-auth',
|
||||
type=str,
|
||||
default=DEFAULT_BASIC_AUTH,
|
||||
help='Default: No authentication. Specify colon separated user:password '
|
||||
'to enable basic authentication.')
|
||||
flags.add_argument(
|
||||
'--version',
|
||||
'-v',
|
||||
|
@ -360,9 +354,11 @@ class Proxy:
|
|||
@staticmethod
|
||||
def get_default_plugins(
|
||||
args: argparse.Namespace) -> List[Tuple[str, bool]]:
|
||||
# Prepare list of plugins to load based upon --enable-* and --disable-*
|
||||
# flags
|
||||
# Prepare list of plugins to load based upon
|
||||
# --enable-*, --disable-* and --basic-auth flags.
|
||||
default_plugins: List[Tuple[str, bool]] = []
|
||||
if args.basic_auth is not None:
|
||||
default_plugins.append((PLUGIN_PROXY_AUTH, True))
|
||||
if args.enable_dashboard:
|
||||
default_plugins.append((PLUGIN_WEB_SERVER, True))
|
||||
args.enable_static_server = True
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
:copyright: (c) 2013-present by Abhinav Singh and contributors.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from proxy.common.utils import bytes_
|
||||
from proxy.common.constants import PLUGIN_HTTP_PROXY
|
||||
import unittest
|
||||
|
||||
from typing import List, Dict
|
||||
|
@ -86,7 +88,7 @@ class TestFlags(unittest.TestCase):
|
|||
|
||||
def test_unique_plugin_from_bytes(self) -> None:
|
||||
self.flags = Proxy.initialize([], plugins=[
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
])
|
||||
self.assert_plugins({'HttpProtocolHandlerPlugin': [
|
||||
HttpProxyPlugin,
|
||||
|
@ -94,7 +96,7 @@ class TestFlags(unittest.TestCase):
|
|||
|
||||
def test_unique_plugin_from_args(self) -> None:
|
||||
self.flags = Proxy.initialize([
|
||||
'--plugins', 'proxy.http.proxy.HttpProxyPlugin',
|
||||
'--plugins', PLUGIN_HTTP_PROXY,
|
||||
])
|
||||
self.assert_plugins({'HttpProtocolHandlerPlugin': [
|
||||
HttpProxyPlugin,
|
||||
|
|
|
@ -18,7 +18,7 @@ from unittest import mock
|
|||
from proxy.proxy import Proxy
|
||||
from proxy.common.version import __version__
|
||||
from proxy.common.utils import bytes_
|
||||
from proxy.common.constants import CRLF
|
||||
from proxy.common.constants import CRLF, PLUGIN_HTTP_PROXY, PLUGIN_PROXY_AUTH, PLUGIN_WEB_SERVER
|
||||
from proxy.core.connection import TcpClientConnection
|
||||
from proxy.http.parser import HttpParser
|
||||
from proxy.http.proxy import HttpProxyPlugin
|
||||
|
@ -41,8 +41,8 @@ class TestHttpProtocolHandler(unittest.TestCase):
|
|||
self.http_server_port = 65535
|
||||
self.flags = Proxy.initialize()
|
||||
self.flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
])
|
||||
|
||||
self.mock_selector = mock_selector
|
||||
|
@ -175,8 +175,9 @@ class TestHttpProtocolHandler(unittest.TestCase):
|
|||
flags = Proxy.initialize(
|
||||
auth_code=base64.b64encode(b'user:pass'))
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
bytes_(PLUGIN_PROXY_AUTH),
|
||||
])
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
TcpClientConnection(self._conn, self._addr), flags=flags)
|
||||
|
@ -208,8 +209,8 @@ class TestHttpProtocolHandler(unittest.TestCase):
|
|||
flags = Proxy.initialize(
|
||||
auth_code=base64.b64encode(b'user:pass'))
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
])
|
||||
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
|
@ -257,8 +258,8 @@ class TestHttpProtocolHandler(unittest.TestCase):
|
|||
flags = Proxy.initialize(
|
||||
auth_code=base64.b64encode(b'user:pass'))
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin'
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER)
|
||||
])
|
||||
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
|
|
|
@ -20,7 +20,7 @@ from proxy.core.connection import TcpClientConnection
|
|||
from proxy.http.handler import HttpProtocolHandler
|
||||
from proxy.http.parser import httpParserStates
|
||||
from proxy.common.utils import build_http_response, build_http_request, bytes_, text_
|
||||
from proxy.common.constants import CRLF, PROXY_PY_DIR
|
||||
from proxy.common.constants import CRLF, PLUGIN_HTTP_PROXY, PLUGIN_PAC_FILE, PLUGIN_WEB_SERVER, PROXY_PY_DIR
|
||||
from proxy.http.server import HttpWebServerPlugin
|
||||
|
||||
|
||||
|
@ -35,8 +35,8 @@ class TestWebServerPlugin(unittest.TestCase):
|
|||
self.mock_selector = mock_selector
|
||||
self.flags = Proxy.initialize()
|
||||
self.flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
])
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
TcpClientConnection(self._conn, self._addr),
|
||||
|
@ -98,8 +98,8 @@ class TestWebServerPlugin(unittest.TestCase):
|
|||
data=None), selectors.EVENT_READ), ]
|
||||
flags = Proxy.initialize()
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
])
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
TcpClientConnection(self._conn, self._addr),
|
||||
|
@ -151,8 +151,8 @@ class TestWebServerPlugin(unittest.TestCase):
|
|||
enable_static_server=True,
|
||||
static_server_dir=static_server_dir)
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
])
|
||||
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
|
@ -201,8 +201,8 @@ class TestWebServerPlugin(unittest.TestCase):
|
|||
|
||||
flags = Proxy.initialize(enable_static_server=True)
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
])
|
||||
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
|
@ -239,9 +239,9 @@ class TestWebServerPlugin(unittest.TestCase):
|
|||
def init_and_make_pac_file_request(self, pac_file: str) -> None:
|
||||
flags = Proxy.initialize(pac_file=pac_file)
|
||||
flags.plugins = Proxy.load_plugins([
|
||||
b'proxy.http.proxy.HttpProxyPlugin',
|
||||
b'proxy.http.server.HttpWebServerPlugin',
|
||||
b'proxy.http.server.HttpWebServerPacFilePlugin',
|
||||
bytes_(PLUGIN_HTTP_PROXY),
|
||||
bytes_(PLUGIN_WEB_SERVER),
|
||||
bytes_(PLUGIN_PAC_FILE),
|
||||
])
|
||||
self.protocol_handler = HttpProtocolHandler(
|
||||
TcpClientConnection(self._conn, self._addr),
|
||||
|
|
Loading…
Reference in New Issue