diff --git a/src/Server.py b/src/Server.py index 8307880..98275d1 100644 --- a/src/Server.py +++ b/src/Server.py @@ -1188,11 +1188,13 @@ def export_license_request_lic(config: Dict[str, Any]) -> Tuple[Dict[str, Any], ) -def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: +def get_active_licensed_features(db_path: Optional[str] = None) -> List[str]: """ - Checks whether a specific enterprise capability (e.g. 'enterprise_db') is unlocked - either via custom X.509 certificate extensions or the persistent license configuration. + Returns the list of all active enterprise capability identifiers + authorized either via X.509 certificate extensions or the persistent database license config. """ + features = set() + # 1. Check in-memory / active Subordinate CA certificate ca_pem = SERVER_STATE.get("ca_cert_pem") if not ca_pem: @@ -1208,9 +1210,9 @@ def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: if ca_pem: try: info = enrollment.extract_license_info_from_cert(ca_pem) - cert_features = info.get("features", []) - if feature in cert_features: - return True + for feat in info.get("features", []): + if feat: + features.add(str(feat).strip()) except Exception: pass @@ -1230,14 +1232,24 @@ def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: feat_list = json.loads(feat_raw) else: feat_list = [f.strip() for f in feat_raw.split(",") if f.strip()] - if feature in feat_list: - return True - elif isinstance(feat_raw, list) and feature in feat_raw: - return True + for f_item in feat_list: + if f_item: + features.add(str(f_item).strip()) + elif isinstance(feat_raw, list): + for f_item in feat_raw: + if f_item: + features.add(str(f_item).strip()) except Exception: pass - return False + return sorted(list(features)) + + +def is_feature_licensed(feature: str, db_path: Optional[str] = None) -> bool: + """Checks whether a specific enterprise capability (e.g. 'enterprise_db') is authorized.""" + if not feature: + return False + return feature in get_active_licensed_features(db_path) def get_server_public_key() -> Optional[pgpy.PGPKey]: @@ -2863,7 +2875,8 @@ def get_license_summary() -> Dict[str, Any]: "days_remaining": info.get("days_remaining", 0), "fingerprint": info.get("fingerprint", "N/A"), "csr_exists": csr_exists, - "server_fingerprint": config.get("server_fingerprint", "N/A") + "server_fingerprint": config.get("server_fingerprint", "N/A"), + "features": get_active_licensed_features(db_path) } @@ -3396,6 +3409,55 @@ def serve_webui(request: Request = None): pg_disabled_attr = "" if is_db_unlocked else "disabled" pg_label_style = "" if is_db_unlocked else "opacity: 0.55; cursor: not-allowed;" + active_features = summary.get("features", []) + feature_cards = [] + if active_features: + for feat in active_features: + if feat == "enterprise_db": + feature_cards.append(""" +
enterprise_db capability package.