Skip to content

Commit 0d5b0bc

Browse files
authored
Client mode: LDAP user_id bug fix (#148)
By default, the login passed to the Nextcloud class was taken as the user name. If a backend other than the Database is used, these two values do not match each other, that is, the login can be: "milena.kvitkova@domain.com", and the user ID for making API requests will be: "milena" Note: This bug does not apply to the NextcloudApp class, because there the AppAPI transmits the user ID, and not the user login - this only affects use as a client. --------- Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
1 parent e411fca commit 0d5b0bc

File tree

4 files changed

+39
-16
lines changed

4 files changed

+39
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This release contains some breaking changes in `users`, `notifications` API.
99

1010
### Added
1111

12-
- `__repr__` method added for most objects(previously it was only present for `FsNode`).
12+
- `__repr__` method added for most objects(previously it was only present for `FsNode`). #147
1313

1414
### Changed
1515

@@ -21,6 +21,7 @@ This release contains some breaking changes in `users`, `notifications` API.
2121
### Fixed
2222

2323
- `users.get_details` with empty parameter in some cases was raised exception.
24+
- ClientMode: in case when LDAP was used as user backend, user login differs from user_id and most API failed with 404. #148
2425

2526
## [0.3.1 - 2023-10-07]
2627

nc_py_api/_session.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@ class NcSessionBasic(ABC):
134134
adapter: Client
135135
adapter_dav: Client
136136
cfg: BasicConfig
137-
user: str
138137
custom_headers: dict
139-
_capabilities: dict
140138
response_headers: HttpxHeaders
139+
_user: str
140+
_capabilities: dict
141141

142142
@abstractmethod
143143
def __init__(self, **kwargs):
144144
self._capabilities = {}
145-
self.user = kwargs.get("user", "")
145+
self._user = kwargs.get("user", "")
146146
self.custom_headers = kwargs.get("headers", {})
147147
self.limits = Limits(max_keepalive_connections=20, max_connections=20, keepalive_expiry=60.0)
148148
self.init_adapter()
@@ -168,22 +168,33 @@ def _get_stream(self, path_params: str, headers: dict, **kwargs) -> Iterator[Res
168168
"GET", f"{self.cfg.endpoint}{path_params}", headers=headers, timeout=timeout, **kwargs
169169
)
170170

171-
def request_json(
171+
def request(
172172
self,
173173
method: str,
174174
path: str,
175175
params: Optional[dict] = None,
176176
data: Optional[Union[bytes, str]] = None,
177177
json: Optional[Union[dict, list]] = None,
178178
**kwargs,
179-
) -> dict:
179+
):
180180
method = method.upper()
181181
if params is None:
182182
params = {}
183183
params.update({"format": "json"})
184184
headers = kwargs.pop("headers", {})
185185
data_bytes = self.__data_to_bytes(headers, data, json)
186-
r = self._ocs(method, f"{quote(path)}?{urlencode(params, True)}", headers, data_bytes, not_parse=True)
186+
return self._ocs(method, f"{quote(path)}?{urlencode(params, True)}", headers, data_bytes, not_parse=True)
187+
188+
def request_json(
189+
self,
190+
method: str,
191+
path: str,
192+
params: Optional[dict] = None,
193+
data: Optional[Union[bytes, str]] = None,
194+
json: Optional[Union[dict, list]] = None,
195+
**kwargs,
196+
) -> dict:
197+
r = self.request(method, path, params, data, json, **kwargs)
187198
return loads(r.text) if r.status_code != 304 else {}
188199

189200
def ocs(
@@ -319,6 +330,17 @@ def _create_adapter(self) -> Client:
319330
def update_server_info(self) -> None:
320331
self._capabilities = self.ocs(method="GET", path="/ocs/v1.php/cloud/capabilities")
321332

333+
@property
334+
def user(self) -> str:
335+
"""Current user ID. Can be different from the login name."""
336+
if isinstance(self, NcSession) and not self._user: # do not trigger for NextcloudApp
337+
self._user = self.ocs(method="GET", path="/ocs/v1.php/cloud/user")["id"]
338+
return self._user
339+
340+
@user.setter
341+
def user(self, value: str):
342+
self._user = value
343+
322344
@property
323345
def capabilities(self) -> dict:
324346
if not self._capabilities:
@@ -360,7 +382,7 @@ class NcSession(NcSessionBasic):
360382

361383
def __init__(self, **kwargs):
362384
self.cfg = Config(**kwargs)
363-
super().__init__(user=self.cfg.auth[0])
385+
super().__init__()
364386

365387
def _create_adapter(self) -> Client:
366388
return Client(auth=self.cfg.auth, follow_redirects=True, limits=self.limits, verify=self.cfg.options.nc_cert)
@@ -401,7 +423,7 @@ def _create_adapter(self) -> Client:
401423
return adapter
402424

403425
def sign_request(self, headers: dict) -> None:
404-
headers["AUTHORIZATION-APP-API"] = b64encode(f"{self.user}:{self.cfg.app_secret}".encode("UTF=8"))
426+
headers["AUTHORIZATION-APP-API"] = b64encode(f"{self._user}:{self.cfg.app_secret}".encode("UTF=8"))
405427

406428
def sign_check(self, request: Request) -> None:
407429
headers = {

nc_py_api/nextcloud.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def __init__(self, **kwargs):
122122

123123
@property
124124
def user(self) -> str:
125-
"""Returns current user name."""
125+
"""Returns current user ID."""
126126
return self._session.user
127127

128128

@@ -184,7 +184,7 @@ def scope_allowed(self, scope: ApiScope) -> bool:
184184

185185
@property
186186
def user(self) -> str:
187-
"""Property containing the current username.
187+
"""Property containing the current user ID.
188188
189189
*System Applications* can set it and impersonate the user. For normal applications, it is set automatically.
190190
"""

tests/actual_tests/user_status_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,14 @@ def test_set_predefined(nc, clear_at):
106106

107107

108108
@pytest.mark.require_nc(major=27)
109-
def test_get_back_status_from_from_empty_user(nc):
110-
orig_user = nc._session.user
111-
nc._session.user = ""
109+
def test_get_back_status_from_from_empty_user(nc_app):
110+
orig_user = nc_app._session.user
111+
nc_app._session.user = ""
112112
try:
113113
with pytest.raises(ValueError):
114-
nc.user_status.get_backup_status("")
114+
nc_app.user_status.get_backup_status("")
115115
finally:
116-
nc._session.user = orig_user
116+
nc_app._session.user = orig_user
117117

118118

119119
@pytest.mark.require_nc(major=27)

0 commit comments

Comments
 (0)