Skip to content

Commit b5e0c70

Browse files
authored
[Fix] nacos server修改nacos.server.contextPath导致注册失败问题 #300 (#312)
* fix(auth): include context path in login URL to fix token retrieval - Fixed 'get access token failed' error when nacos.server.contextPath is customized - Updated auth_client.py to correctly construct the login URL with the server context path - Ensures compatibility with Nacos Server 3.0.3+ custom context configurations - Resolves issue #300 * test: add context_path URL regression tests for issue #300
1 parent 12d7cff commit b5e0c70

3 files changed

Lines changed: 103 additions & 4 deletions

File tree

test/client_v2_test.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
import unittest
44
from typing import List
5+
from unittest.mock import AsyncMock
6+
from unittest.mock import AsyncMock, MagicMock
57

68
from v2.nacos import ConfigParam
79
from v2.nacos.common.client_config import GRPCConfig
@@ -12,6 +14,8 @@
1214
from v2.nacos.naming.nacos_naming_service import NacosNamingService
1315
from v2.nacos.config.nacos_config_service import NacosConfigService
1416
from v2.nacos.common.auth import CredentialsProvider, Credentials
17+
from v2.nacos.common.client_config import ClientConfig
18+
from v2.nacos.transport.auth_client import AuthClient
1519

1620
client_config = (ClientConfigBuilder()
1721
.access_key(os.getenv('NACOS_ACCESS_KEY'))
@@ -189,6 +193,90 @@ async def cb(instance_list: List[Instance]):
189193

190194
await client.shutdown()
191195

196+
async def test_auth_login_url_with_root_context_path(self):
197+
"""
198+
[Issue #300] Verifies that when context_path is '/', the login URL
199+
is correctly formed as '/v1/auth/users/login' without double slashes.
200+
"""
201+
202+
# 1. Setup config with root context path
203+
config = ClientConfig(
204+
server_addresses="http://127.0.0.1:8848",
205+
username="nacos",
206+
password="nacos",
207+
context_path="/"
208+
)
209+
210+
# 2. Create required mock objects
211+
mock_get_server_list = MagicMock(return_value=["http://127.0.0.1:8848"])
212+
mock_http_agent = MagicMock()
213+
mock_logger = MagicMock() # Mock logger
214+
215+
# 3. Initialize AuthClient with ALL required arguments
216+
client = AuthClient(
217+
client_config=config,
218+
get_server_list_func=mock_get_server_list,
219+
http_agent=mock_http_agent,
220+
logger=mock_logger
221+
)
222+
223+
# 4. Mock the HTTP request to capture the URL
224+
mock_response = (b'{"accessToken":"mock-token", "tokenTtl":18000}', None)
225+
mock_request = AsyncMock(return_value=mock_response)
226+
mock_http_agent.request = mock_request
227+
228+
# 5. Execute login logic
229+
await client.get_access_token(force_refresh=True)
230+
231+
# 6. Assert the generated URL
232+
called_url = mock_request.call_args[0][0]
233+
expected_url = "http://127.0.0.1:8848/v1/auth/users/login"
234+
235+
self.assertEqual(called_url, expected_url,
236+
f"URL mismatch for root context_path. Expected '{expected_url}', but got '{called_url}'")
237+
238+
async def test_auth_login_url_with_standard_context_path(self):
239+
"""
240+
[Regression Test] Verifies that when context_path is '/nacos',
241+
the login URL correctly includes the prefix.
242+
"""
243+
import logging
244+
245+
# 1. Setup config with standard context path
246+
config = ClientConfig(
247+
server_addresses="http://127.0.0.1:8848",
248+
username="nacos",
249+
password="nacos",
250+
context_path="/nacos"
251+
)
252+
253+
# 2. Create required mock objects
254+
mock_get_server_list = MagicMock(return_value=["http://127.0.0.1:8848"])
255+
mock_http_agent = MagicMock()
256+
mock_logger = MagicMock() # Mock logger
257+
258+
# 3. Initialize AuthClient with ALL required arguments
259+
client = AuthClient(
260+
client_config=config,
261+
get_server_list_func=mock_get_server_list,
262+
http_agent=mock_http_agent,
263+
logger=mock_logger
264+
)
265+
266+
# 4. Mock the HTTP request
267+
mock_response = (b'{"accessToken":"mock-token", "tokenTtl":18000}', None)
268+
mock_request = AsyncMock(return_value=mock_response)
269+
mock_http_agent.request = mock_request
270+
271+
# 5. Execute login logic
272+
await client.get_access_token(force_refresh=True)
273+
274+
# 6. Assert the generated URL
275+
called_url = mock_request.call_args[0][0]
276+
expected_url = "http://127.0.0.1:8848/nacos/v1/auth/users/login"
277+
278+
self.assertEqual(called_url, expected_url,
279+
f"URL mismatch for standard context_path. Expected '{expected_url}', but got '{called_url}'")
192280

193281
if __name__ == '__main__':
194282
unittest.main()

v2/nacos/common/client_config.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def __init__(self, max_receive_message_length=Constants.GRPC_MAX_RECEIVE_MESSAGE
4747

4848

4949
class ClientConfig:
50-
def __init__(self, server_addresses=None, endpoint=None, namespace_id='', context_path='', access_key=None,
50+
def __init__(self, server_addresses=None, endpoint=None, namespace_id='', context_path=Constants.WEB_CONTEXT, access_key=None,
5151
secret_key=None, username=None, password=None, app_name='', app_key='', log_dir='', log_level=None,
5252
log_rotation_backup_count=None, app_conn_labels=None, credentials_provider=None):
5353
self.server_list = []
@@ -63,7 +63,15 @@ def __init__(self, server_addresses=None, endpoint=None, namespace_id='', contex
6363
self.endpoint_query_header = None
6464
self.namespace_id = namespace_id
6565
self.credentials_provider = credentials_provider if credentials_provider else StaticCredentialsProvider(access_key, secret_key)
66-
self.context_path = context_path
66+
if not context_path:
67+
self.context_path = Constants.WEB_CONTEXT
68+
else:
69+
cp = context_path
70+
if not cp.startswith("/"):
71+
cp = "/" + cp
72+
if cp != "/" and cp.endswith("/"):
73+
cp = cp[:-1]
74+
self.context_path = cp
6775
self.username = username # the username for nacos auth
6876
self.password = password # the password for nacos auth
6977
self.app_name = app_name

v2/nacos/transport/auth_client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ async def get_access_token(self, force_refresh=False):
2828
"username": self.username,
2929
"password": self.password
3030
}
31-
31+
ctx_path = self.client_config.context_path
3232
server_list = self.get_server_list()
3333
for server_address in server_list:
34-
url = server_address + "/nacos/v1/auth/users/login"
34+
if ctx_path == "/":
35+
url = server_address + "/v1/auth/users/login"
36+
else:
37+
url = server_address + ctx_path + "/v1/auth/users/login"
3538
resp, error = await self.http_agent.request(url, "POST", None, params, None)
3639
if not resp or error:
3740
self.logger.warning(f"[get-access-token] request {url} failed, error: {error}")

0 commit comments

Comments
 (0)