diff --git a/examples/upgrade_server/npbackup_upgrade_server.conf.dist b/examples/upgrade_server/npbackup_upgrade_server.conf.dist index de79f87..a600090 100644 --- a/examples/upgrade_server/npbackup_upgrade_server.conf.dist +++ b/examples/upgrade_server/npbackup_upgrade_server.conf.dist @@ -3,18 +3,23 @@ http_server: listen: 0.0.0.0 port: 8080 - username: upgrade_client - password: super_secret_password + users: + - upgrade_client: + password: super_secret_password + permissions: + - audience: + - private + - public upgrades: # Build dir should contain the following structure # /VERSION # VERSION is a file containing a single line with the currently built NPBackup version, example: 2.2.0 - # /{platform}/{arch}/{binary} + # /{platform}/{arch}/{binary}/{audience} # Current platforms are 'windows', 'linux' # Current arches are 'x64', 'x86', 'arm' and 'arm64' # In each folder there should be a npbackup or npbackup.exe binary depending on the platform - data_root: /var/www/upgrade_server/dist + data_root: /var/npbackup_upgrade_server/dist # We'll store a CSV containing backup clients that upgrade here - statistics_file: /var/www/upgrade_server/stats.csv + statistics_file: /var/npbackup_upgrade_server/stats.csv diff --git a/upgrade_server/upgrade_server/api.py b/upgrade_server/upgrade_server/api.py index 5b373b9..79cd4e8 100644 --- a/upgrade_server/upgrade_server/api.py +++ b/upgrade_server/upgrade_server/api.py @@ -60,17 +60,20 @@ security = HTTPBasic() def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): - current_username_bytes = credentials.username.encode("utf8") - correct_username_bytes = config_dict["http_server"]["username"].encode("utf-8") - is_correct_username = secrets.compare_digest( - current_username_bytes, correct_username_bytes - ) - current_password_bytes = credentials.password.encode("utf8") - correct_password_bytes = config_dict["http_server"]["password"].encode("utf-8") - is_correct_password = secrets.compare_digest( - current_password_bytes, correct_password_bytes - ) - if not (is_correct_username and is_correct_password): + authenticated_user = None + + + for user in config_dict["http_server"]["users"]: + try: + if secrets.compare_digsest(credentials.username.encode("utf-8"), user.encode("utf-8")): + if secrets.compare_digest(credentials.password.encode("utf-8"), config_dict["http_server"]["users"]["user"]["password"].encode("utf-8")): + authenticated_user = user + break + except Exception as exc: + logger.info(f"Failed to check user: {exc}") + + + if authenticated_user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password", @@ -79,6 +82,17 @@ def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): return credentials.username +def get_user_permissions(username: str): + """ + Returns a list of permissions + """ + try: + return config_dict["http_server"]["users"][username]["permissions"] + except Exception as exc: + logger.error(f"Failed to get user permissions: {exc}") + return [] + + @app.get("/") async def api_root(auth=Depends(get_current_username)): if crud.is_enabled(): @@ -139,9 +153,18 @@ async def current_version( client_ip = x_forwarded_for else: client_ip = request.client.host + + try: + has_permission = True if audience.value in get_user_permissions(auth)["audience"] else False + except Exception as exc: + logger.error(f"Failed to get user permissions: {exc}") + has_permission = False + data = { "action": "check_version", "ip": client_ip, + "user": auth, + "has_permission": has_permission, "auto_upgrade_host_identity": auto_upgrade_host_identity, "installed_version": installed_version, "group": group, @@ -156,6 +179,12 @@ async def current_version( except KeyError: logger.error("No statistics file set.") + if not has_permission: + raise HTTPException( + status_code=403, + detail="User does not have permission to access this resource", + ) + if not crud.is_enabled(): return CurrentVersion(version="0.00") @@ -222,9 +251,18 @@ async def upgrades( client_ip = x_forwarded_for else: client_ip = request.client.host + + try: + has_permission = True if audience.value in get_user_permissions(auth)["audience"] else False + except Exception as exc: + logger.error(f"Failed to get user permissions: {exc}") + has_permission = False + data = { "action": "get_file_info", "ip": client_ip, + "user": auth, + "has_permission": has_permission, "auto_upgrade_host_identity": auto_upgrade_host_identity, "installed_version": installed_version, "group": group, @@ -239,6 +277,12 @@ async def upgrades( except KeyError: logger.error("No statistics file set.") + if not has_permission: + raise HTTPException( + status_code=403, + detail="User does not have permission to access this resource", + ) + if not crud.is_enabled(): raise HTTPException( status_code=503, detail="Service is currently disabled for maintenance" @@ -307,9 +351,18 @@ async def download( client_ip = x_forwarded_for else: client_ip = request.client.host + + try: + has_permission = True if audience.value in get_user_permissions(auth)["audience"] else False + except Exception as exc: + logger.error(f"Failed to get user permissions: {exc}") + has_permission = False + data = { "action": "download_upgrade", "ip": client_ip, + "user": auth, + "has_permission": has_permission, "auto_upgrade_host_identity": auto_upgrade_host_identity, "installed_version": installed_version, "group": group, @@ -324,6 +377,12 @@ async def download( except KeyError: logger.error("No statistics file set.") + if not has_permission: + raise HTTPException( + status_code=403, + detail="User does not have permission to access this resource", + ) + if not crud.is_enabled(): raise HTTPException( status_code=503, detail="Service is currently disabled for maintenance"