diff --git a/src/confd_gnmi_api_adapter.py b/src/confd_gnmi_api_adapter.py index 368b2f6..0851fb1 100644 --- a/src/confd_gnmi_api_adapter.py +++ b/src/confd_gnmi_api_adapter.py @@ -36,6 +36,13 @@ class GnmiConfDApiServerAdapter(GnmiServerAdapter): monitor_external_changes: bool = ApiAdapterDefaults.MONITOR_EXTERNAL_CHANGES external_port: int = ApiAdapterDefaults.EXTERNAL_PORT + class AuthenticationException(Exception): + def __init__(self, user, reason="N/A"): + self.user = user + self.reason = reason + def __str__(self): + return f"Authentication failed for user '{self.user}': {self.reason}." + def __init__(self): self.addr: str = GnmiConfDApiServerAdapter.addr self.port: int = GnmiConfDApiServerAdapter.port @@ -404,10 +411,21 @@ def get_subscription_handler(self, log.debug("<== handler=%s", handler) return handler - def set_credentials(self, username="admin", password="admin"): + def authenticate(self, username="admin", password="admin"): log.info("==> username=%s password=:-)", username) self.username = username self.password = password + auth_status = maapi.Maapi().authenticate(self.username, self.password, 1) + reason = "N/A" + if not isinstance(auth_status, int): + reason = auth_status[1] + auth_status = auth_status[0] + if auth_status == 1: + log.info(f"Authenticated {self.username}.") + else: + e = self.AuthenticationException(self.username, str(reason)) + log.warning(e) + raise e log.info("<== self.username=%s self.password=:-)", self.username) # https://tools.ietf.org/html/rfc6022#page-8 @@ -536,7 +554,7 @@ def make_updates_with_maagic_rec(self, tr, node): elif isinstance(node, maagic.Leaf): yield from self.append_update(tr, node._path, node._cs_node) else: - if hasattr(node, "_children"): # skip nodes w/o children, e.g. Action + if hasattr(node, "_children"): # skip nodes w/o children, e.g. Action children = node._children.get_children(node._backend, node) if len(children) == 0 and isinstance(node, maagic.PresenceContainer): diff --git a/src/confd_gnmi_servicer.py b/src/confd_gnmi_servicer.py index aac5744..406c749 100755 --- a/src/confd_gnmi_servicer.py +++ b/src/confd_gnmi_servicer.py @@ -42,7 +42,7 @@ def get_and_connect_adapter(self, username, password): elif self.adapter_type == AdapterType.API: from confd_gnmi_api_adapter import GnmiConfDApiServerAdapter adapter = GnmiConfDApiServerAdapter.get_adapter() - adapter.set_credentials(username=username, password=password) + adapter.authenticate(username=username, password=password) log.debug("<== adapter=%s", adapter) return adapter diff --git a/tests/test_client_server_confd.py b/tests/test_client_server_confd.py index e8f20e3..fe3944e 100644 --- a/tests/test_client_server_confd.py +++ b/tests/test_client_server_confd.py @@ -6,10 +6,12 @@ import pytest +import grpc import gnmi_pb2 from client_server_test_base import GrpcBase from confd_gnmi_common import make_gnmi_path, make_xpath_path from confd_gnmi_server import AdapterType +from confd_gnmi_client import ConfDgNMIClient from route_status import RouteData, RouteProvider, ChangeOp from utils.utils import log sys.path.append(os.getenv('CONFD_DIR')+"/src/confd/pyapi/confd") @@ -177,3 +179,17 @@ def test_subscribe_stream_on_change_api_state(self, request): RouteProvider.stop_confd_loop() confd_thread.join() RouteProvider.close_dp() + + def _assert_auth(self, err_string, username="admin", password="admin"): + client = ConfDgNMIClient(username=username, password=password, insecure=True) + with pytest.raises(grpc.RpcError) as err: + capabilities = client.get_capabilities() + client.close() + assert err_string in str(err) + + @pytest.mark.confd + def test_authentication(self, request): + log.info("testing authentication") + self._assert_auth("Bad password", password="bad") + self._assert_auth("No such local user", username="bad", password="bad") + self._assert_auth("No such local user", username="bad")