Clean up of IP checking
All checks were successful
docker-image / docker (push) Successful in 1m28s

This commit is contained in:
Stefan Bethke 2025-05-30 10:31:30 +02:00
commit 6245c11a07
3 changed files with 16 additions and 21 deletions

View file

@ -1,8 +1,7 @@
from ipaddress import ip_address, IPv4Address, ip_network from ipaddress import ip_network
from typing import Callable, List from typing import Callable
from BottleOIDC import BottleOIDC from BottleOIDC import BottleOIDC
from BottleOIDC.bottle_utils import UnauthorizedError
from bottle import request, abort from bottle import request, abort
@ -15,11 +14,6 @@ class BottleHelpers:
self.auth = auth self.auth = auth
self.group = group self.group = group
def remote_addr(self):
if request.remote_route is not None:
return request.remote_route[-1]
return request.remote_addr
def require_login(self, func: Callable) -> Callable: def require_login(self, func: Callable) -> Callable:
if self.group is not None: if self.group is not None:
return self.auth.require_login(self.require_attribute('groups', self.group)(func)) return self.auth.require_login(self.require_attribute('groups', self.group)(func))
@ -41,16 +35,12 @@ class BottleHelpers:
return False return False
def _outer_wrapper(f): def _outer_wrapper(f):
def _wrapper(*args, **kwargs): def _wrapper(*args, **kwargs):
if attr in self.auth.my_attrs: if attr in self.auth.my_attrs:
resource = request.session[self.auth.sess_attr][attr] resource = request.session[self.auth.sess_attr][attr]
if test_attrs(resource, value): if test_attrs(resource, value):
return f(*args, **kwargs) return f(*args, **kwargs)
abort(401, 'Not Authorized: Not In Group')
abort(401, 'Not Authorized')
_wrapper.__name__ = f.__name__ _wrapper.__name__ = f.__name__
return _wrapper return _wrapper
@ -65,7 +55,7 @@ class BottleHelpers:
def _wrapper(*args, **kwargs): def _wrapper(*args, **kwargs):
if self.auth.my_username is not None: if self.auth.my_username is not None:
return f(*args, **kwargs) return f(*args, **kwargs)
abort(401, 'Not Authorized') abort(401, 'Not Authorized: Not logged in')
return None return None
_wrapper.__name__ = f.__name__ _wrapper.__name__ = f.__name__
@ -83,7 +73,7 @@ class BottleHelpers:
for allowed in self.allowed: for allowed in self.allowed:
if addr.overlaps(allowed): if addr.overlaps(allowed):
return f(*args, **kwargs) return f(*args, **kwargs)
abort(401, 'Not Authorized') abort(401, 'Not Authorized: Wrong IP')
return None return None
_wrapper.__name__ = f.__name__ _wrapper.__name__ = f.__name__

View file

@ -3,12 +3,10 @@ FastAPI main entry point
""" """
import json import json
import logging import logging
from typing import Callable
from BottleOIDC import BottleOIDC from BottleOIDC import BottleOIDC
from BottleOIDC.bottle_utils import UnauthorizedError
from BottleSessions import BottleSessions from BottleSessions import BottleSessions
from bottle import route, run, Bottle, static_file, TEMPLATE_PATH, jinja2_view, post, get, request, error from bottle import Bottle, static_file, TEMPLATE_PATH, jinja2_view, request
from bottle_log import LoggingPlugin from bottle_log import LoggingPlugin
from bottle_websocket import websocket, GeventWebSocketServer from bottle_websocket import websocket, GeventWebSocketServer
from geventwebsocket.websocket import WebSocket from geventwebsocket.websocket import WebSocket
@ -54,6 +52,7 @@ def server_static(filepath):
def root(): def root():
return {} return {}
@app.get("/operate") @app.get("/operate")
@bottle_helpers.require_sourceip @bottle_helpers.require_sourceip
@bottle_helpers.require_login @bottle_helpers.require_login
@ -75,20 +74,24 @@ def websocket_endpoint(ws: WebSocket):
finally: finally:
websocket_clients.remove(ws) websocket_clients.remove(ws)
@app.get('/api/lock') @app.get('/api/lock')
def get_api_lock(): def get_api_lock():
return update_poller.get_locks(True) return update_poller.get_locks(True)
@app.get('/api/lock/<id>') @app.get('/api/lock/<id>')
def get_api_lock(id): def get_api_lock(id):
return update_poller.get_lock(id) return update_poller.get_lock(id)
@app.post('/api/lock/<id>') @app.post('/api/lock/<id>')
@bottle_helpers.require_sourceip @bottle_helpers.require_sourceip
@bottle_helpers.require_authz @bottle_helpers.require_authz
def post_api_lock(id): def post_api_lock(id):
return ccujack.lock_unlock(id, request.json["locking"]) return ccujack.lock_unlock(id, request.json["locking"])
@app.error(401) @app.error(401)
@jinja2_view("not_authorized.html.j2") @jinja2_view("not_authorized.html.j2")
def not_authorized(error): def not_authorized(error):
@ -98,7 +101,7 @@ def not_authorized(error):
groups = request.session[auth.sess_attr]['groups'] groups = request.session[auth.sess_attr]['groups']
return { return {
'user': auth.my_username, 'user': auth.my_username,
'ip': request.get_header('x-forwarded-for', request.remote_addr), 'ip': request.remote_addr,
'error': error, 'error': error,
'code': code, 'code': code,
'msg': msg, 'msg': msg,
@ -107,4 +110,5 @@ def not_authorized(error):
if __name__ == '__main__': if __name__ == '__main__':
app.run(host=config.listen_host, port=config.listen_port, server=GeventWebSocketServer, debug=config.debug, quiet=not config.debug) app.run(host=config.listen_host, port=config.listen_port, server=GeventWebSocketServer, debug=config.debug,
quiet=not config.debug)

View file

@ -8,6 +8,7 @@
<body> <body>
<h1>HM Dooris - {{ msg }}</h1> <h1>HM Dooris - {{ msg }}</h1>
<p>You are not authorized to lock or unlock.</p> <p>You are not authorized to lock or unlock.</p>
<p>user: {{ user }}, groups: {{ groups }}, ip: {{ ip }}, error: {{ error }}, code: {{ code }}, msg: {{ msg }}</p> <p>user: {{ user }}, groups: {{ groups }}, ip: {{ ip }}, code: {{ code }}, msg: {{ msg }}</p>
<p>{{ headers }}</p>
</body> </body>
</html> </html>