Better unauthorized error page
All checks were successful
docker-image / docker (push) Successful in 1m21s

This commit is contained in:
Stefan Bethke 2025-05-29 17:40:21 +02:00
commit 292185bb7d
3 changed files with 39 additions and 4 deletions

View file

@ -17,19 +17,51 @@ class BottleHelpers:
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.auth.require_attribute('groups', self.group)(func)) return self.auth.require_login(self.require_attribute('groups', self.group)(func))
else: else:
return self.auth.require_login(func) return self.auth.require_login(func)
def require_attribute(self, attr, value):
""" Decorator requires specific attribute value. """
def test_attrs(challenge, standard):
"""Compare list or val the standard."""
stand_list = standard if type(standard) is list else [standard]
chal_list = challenge if type(challenge) is list else [challenge]
for chal in chal_list:
if chal in stand_list:
return True
return False
def _outer_wrapper(f):
def _wrapper(*args, **kwargs):
if attr in self.auth.my_attrs:
resource = request.session[self.auth.sess_attr][attr]
if test_attrs(resource, value):
return f(*args, **kwargs)
abort(401, 'Not Authorized')
_wrapper.__name__ = f.__name__
return _wrapper
return _outer_wrapper
def require_authz(self, func: Callable) -> Callable: def require_authz(self, func: Callable) -> Callable:
if self.group is not None: if self.group is not None:
return self.auth.require_attribute('groups', self.group)(func) return self.require_attribute('groups', self.group)(func)
else: else:
def _outer_wrapper(f): def _outer_wrapper(f):
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')
return None
_wrapper.__name__ = f.__name__ _wrapper.__name__ = f.__name__
return _wrapper return _wrapper
@ -47,6 +79,7 @@ class BottleHelpers:
if addr.overlaps(allowed): if addr.overlaps(allowed):
return f(*args, **kwargs) return f(*args, **kwargs)
abort(401, 'Not Authorized') abort(401, 'Not Authorized')
return None
_wrapper.__name__ = f.__name__ _wrapper.__name__ = f.__name__
return _wrapper return _wrapper

View file

@ -84,6 +84,7 @@ 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_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"])
@ -92,15 +93,16 @@ def post_api_lock(id):
@jinja2_view("not_authorized.html.j2") @jinja2_view("not_authorized.html.j2")
def not_authorized(error): def not_authorized(error):
code, msg = error.args code, msg = error.args
groups = request.session[auth.sess_attr]['groups'] if 'groups' in request.session[auth.sess_attr] else []
return { return {
'user': auth.my_username, 'user': auth.my_username,
'ip': request.remote_addr, 'ip': request.remote_addr,
'error': error, 'error': error,
'code': code, 'code': code,
'msg': msg, 'msg': msg,
'groups': groups,
} }
app.error_handler[401] = not_authorized
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,6 @@
<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 }}, ip: {{ ip }}, error: {{ error }}, code: {{ code }}, msg: {{ msg }}</p> <p>user: {{ user }}, groups: {{ groups }}, ip: {{ ip }}, error: {{ error }}, code: {{ code }}, msg: {{ msg }}</p>
</body> </body>
</html> </html>