diff --git a/nc_py_api/_preferences_ex.py b/nc_py_api/_preferences_ex.py index 355f3b34..e50826dd 100644 --- a/nc_py_api/_preferences_ex.py +++ b/nc_py_api/_preferences_ex.py @@ -61,6 +61,21 @@ def delete(self, keys: str | list[str], not_fail=True) -> None: if not not_fail: raise e from None + def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None: + """Sets a value and if specified the sensitive flag for a key. + + .. note:: A sensitive flag ensures key value are encrypted and truncated in Nextcloud logs. + Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and + sensitive is *unspecified* it will not change the existing `sensitive` flag. + """ + if not key: + raise ValueError("`key` parameter can not be empty") + require_capabilities("app_api", self._session.capabilities) + params: dict = {"configKey": key, "configValue": value} + if sensitive is not None: + params["sensitive"] = sensitive + self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params) + class _AsyncBasicAppCfgPref: _url_suffix: str @@ -104,72 +119,41 @@ async def delete(self, keys: str | list[str], not_fail=True) -> None: if not not_fail: raise e from None + async def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None: + """Sets a value and if specified the sensitive flag for a key. + + .. note:: A sensitive flag ensures key value are encrypted and truncated in Nextcloud logs. + Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and + sensitive is *unspecified* it will not change the existing `sensitive` flag. + """ + if not key: + raise ValueError("`key` parameter can not be empty") + require_capabilities("app_api", await self._session.capabilities) + params: dict = {"configKey": key, "configValue": value} + if sensitive is not None: + params["sensitive"] = sensitive + await self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params) + class PreferencesExAPI(_BasicAppCfgPref): - """User specific preferences API, avalaible as **nc.preferences_ex.**.""" + """User specific preferences API, available as **nc.preferences_ex.**.""" _url_suffix = "ex-app/preference" - def set_value(self, key: str, value: str) -> None: - """Sets a value for a key.""" - if not key: - raise ValueError("`key` parameter can not be empty") - require_capabilities("app_api", self._session.capabilities) - params = {"configKey": key, "configValue": value} - self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params) - class AsyncPreferencesExAPI(_AsyncBasicAppCfgPref): """User specific preferences API.""" _url_suffix = "ex-app/preference" - async def set_value(self, key: str, value: str) -> None: - """Sets a value for a key.""" - if not key: - raise ValueError("`key` parameter can not be empty") - require_capabilities("app_api", await self._session.capabilities) - params = {"configKey": key, "configValue": value} - await self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params) - class AppConfigExAPI(_BasicAppCfgPref): - """Non-user(App) specific preferences API, avalaible as **nc.appconfig_ex.**.""" + """Non-user(App) specific preferences API, available as **nc.appconfig_ex.**.""" _url_suffix = "ex-app/config" - def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None: - """Sets a value and if specified the sensitive flag for a key. - - .. note:: A sensitive flag ensures key values are truncated in Nextcloud logs. - Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and - sensitive is *unspecified* it will not change the existing `sensitive` flag. - """ - if not key: - raise ValueError("`key` parameter can not be empty") - require_capabilities("app_api", self._session.capabilities) - params: dict = {"configKey": key, "configValue": value} - if sensitive is not None: - params["sensitive"] = sensitive - self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params) - class AsyncAppConfigExAPI(_AsyncBasicAppCfgPref): """Non-user(App) specific preferences API.""" _url_suffix = "ex-app/config" - - async def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None: - """Sets a value and if specified the sensitive flag for a key. - - .. note:: A sensitive flag ensures key values are truncated in Nextcloud logs. - Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and - sensitive is *unspecified* it will not change the existing `sensitive` flag. - """ - if not key: - raise ValueError("`key` parameter can not be empty") - require_capabilities("app_api", await self._session.capabilities) - params: dict = {"configKey": key, "configValue": value} - if sensitive is not None: - params["sensitive"] = sensitive - await self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params) diff --git a/nc_py_api/_version.py b/nc_py_api/_version.py index 61de1f96..8225fef6 100644 --- a/nc_py_api/_version.py +++ b/nc_py_api/_version.py @@ -1,3 +1,3 @@ """Version of nc_py_api.""" -__version__ = "0.20.1" +__version__ = "0.20.2" diff --git a/nc_py_api/ex_app/ui/settings.py b/nc_py_api/ex_app/ui/settings.py index 67405ead..585ce71a 100644 --- a/nc_py_api/ex_app/ui/settings.py +++ b/nc_py_api/ex_app/ui/settings.py @@ -48,6 +48,7 @@ class SettingsField: description: str = "" placeholder: str = "" label: str = "" + sensitive: bool = False notify = False # to be supported in future @classmethod @@ -74,6 +75,7 @@ def to_dict(self) -> dict: "placeholder": self.placeholder, "label": self.label, "notify": self.notify, + "sensitive": self.sensitive, } diff --git a/tests/actual_tests/appcfg_prefs_ex_test.py b/tests/actual_tests/appcfg_prefs_ex_test.py index 209350ca..ae1ca1ea 100644 --- a/tests/actual_tests/appcfg_prefs_ex_test.py +++ b/tests/actual_tests/appcfg_prefs_ex_test.py @@ -233,60 +233,100 @@ async def test_cfg_ex_get_typing_async(class_to_test): assert r[1].value == "321" -def test_appcfg_sensitive(nc_app): - appcfg = nc_app.appconfig_ex - appcfg.delete("test_key") - appcfg.set_value("test_key", "123", sensitive=True) - assert appcfg.get_value("test_key") == "123" - assert appcfg.get_values(["test_key"])[0].value == "123" - appcfg.delete("test_key") +@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex)) +def test_appcfg_sensitive(nc_app, class_to_test): + class_to_test.delete("test_key") + class_to_test.set_value("test_key", "123", sensitive=True) + assert class_to_test.get_value("test_key") == "123" + assert class_to_test.get_values(["test_key"])[0].value == "123" + class_to_test.delete("test_key") # next code tests `sensitive` value from the `AppAPI` params = {"configKey": "test_key", "configValue": "123"} - result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params) - assert not result["sensitive"] # by default if sensitive value is unspecified it is False - appcfg.delete("test_key") - params = {"configKey": "test_key", "configValue": "123", "sensitive": True} - result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params) + result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) + if class_to_test == NC_APP.appconfig_ex or nc_app.srv_version["major"] >= 32: + assert not result["sensitive"] # by default if sensitive value is unspecified it is False + class_to_test.delete("test_key") + params["sensitive"] = True + result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) assert result["configkey"] == "test_key" assert result["configvalue"] == "123" - assert bool(result["sensitive"]) is True + if class_to_test == NC_APP.appconfig_ex or nc_app.srv_version["major"] >= 32: + assert bool(result["sensitive"]) is True params.pop("sensitive") # if we not specify value, AppEcosystem should not change it. - result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params) + result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) assert result["configkey"] == "test_key" assert result["configvalue"] == "123" - assert bool(result["sensitive"]) is True + if class_to_test == NC_APP.appconfig_ex or nc_app.srv_version["major"] >= 32: + assert bool(result["sensitive"]) is True params["sensitive"] = False - result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params) + result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) assert result["configkey"] == "test_key" assert result["configvalue"] == "123" - assert bool(result["sensitive"]) is False + if class_to_test == NC_APP.appconfig_ex or nc_app.srv_version["major"] >= 32: + assert bool(result["sensitive"]) is False + # test setting to empty value (sensitive=False) + params["configValue"] = "" + result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) + assert result["configkey"] == "test_key" + assert result["configvalue"] == "" + if class_to_test == NC_APP.appconfig_ex or nc_app.srv_version["major"] >= 32: + assert bool(result["sensitive"]) is False + assert class_to_test.get_value("test_key") == "" + # test setting to empty value (sensitive=True) + params["sensitive"] = True + result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) + assert result["configkey"] == "test_key" + assert result["configvalue"] == "" + if class_to_test == NC_APP.appconfig_ex or nc_app.srv_version["major"] >= 32: + assert bool(result["sensitive"]) is True + assert class_to_test.get_value("test_key") == "" @pytest.mark.asyncio(scope="session") -async def test_appcfg_sensitive_async(anc_app): - appcfg = anc_app.appconfig_ex - await appcfg.delete("test_key") - await appcfg.set_value("test_key", "123", sensitive=True) - assert await appcfg.get_value("test_key") == "123" - assert (await appcfg.get_values(["test_key"]))[0].value == "123" - await appcfg.delete("test_key") +@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex)) +async def test_appcfg_sensitive_async(anc_app, class_to_test): + await class_to_test.delete("test_key") + await class_to_test.set_value("test_key", "123", sensitive=True) + assert await class_to_test.get_value("test_key") == "123" + assert (await class_to_test.get_values(["test_key"]))[0].value == "123" + await class_to_test.delete("test_key") # next code tests `sensitive` value from the `AppAPI` params = {"configKey": "test_key", "configValue": "123"} - result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params) - assert not result["sensitive"] # by default if sensitive value is unspecified it is False - await appcfg.delete("test_key") - params = {"configKey": "test_key", "configValue": "123", "sensitive": True} - result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params) + result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) + if class_to_test == NC_APP_ASYNC.appconfig_ex or (await anc_app.srv_version)["major"] >= 32: + assert not result["sensitive"] # by default if sensitive value is unspecified it is False + await class_to_test.delete("test_key") + params["sensitive"] = True + result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) assert result["configkey"] == "test_key" assert result["configvalue"] == "123" - assert bool(result["sensitive"]) is True + if class_to_test == NC_APP_ASYNC.appconfig_ex or (await anc_app.srv_version)["major"] >= 32: + assert bool(result["sensitive"]) is True params.pop("sensitive") # if we not specify value, AppEcosystem should not change it. - result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params) + result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) assert result["configkey"] == "test_key" assert result["configvalue"] == "123" - assert bool(result["sensitive"]) is True + if class_to_test == NC_APP_ASYNC.appconfig_ex or (await anc_app.srv_version)["major"] >= 32: + assert bool(result["sensitive"]) is True params["sensitive"] = False - result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params) + result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) assert result["configkey"] == "test_key" assert result["configvalue"] == "123" - assert bool(result["sensitive"]) is False + if class_to_test == NC_APP_ASYNC.appconfig_ex or (await anc_app.srv_version)["major"] >= 32: + assert bool(result["sensitive"]) is False + # test setting to empty value (sensitive=False) + params["configValue"] = "" + result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) + assert result["configkey"] == "test_key" + assert result["configvalue"] == "" + if class_to_test == NC_APP_ASYNC.appconfig_ex or (await anc_app.srv_version)["major"] >= 32: + assert bool(result["sensitive"]) is False + assert await class_to_test.get_value("test_key") == "" + # test setting to empty value (sensitive=True) + params["sensitive"] = True + result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{class_to_test._url_suffix}", json=params) + assert result["configkey"] == "test_key" + assert result["configvalue"] == "" + if class_to_test == NC_APP_ASYNC.appconfig_ex or (await anc_app.srv_version)["major"] >= 32: + assert bool(result["sensitive"]) is True + assert await class_to_test.get_value("test_key") == ""