Skip to content

Commit

Permalink
Improve API regarding SNMP v3 credentials.
Browse files Browse the repository at this point in the history
The Authentication, Privacy, and related classes now support equality
operations.
The Session.create_users method now assumes it's passed objects of type
UsmUser and excludes incomplete security options from the argument
string.

ZEN-35108
  • Loading branch information
jpeacock-zenoss committed Nov 7, 2024
1 parent a721a28 commit a3d527c
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 20 deletions.
30 changes: 19 additions & 11 deletions pynetsnmp/netsnmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,20 +858,28 @@ def awaitTraps(
def create_users(self, users):
self._log.debug("create_users: Creating %s users.", len(users))
for user in users:
if user.version != SNMP_VERSION_3:
if str(user.version) != str(SNMP_VERSION_3):
self._log.info("create_users: user is not v3 %s", user)
continue
try:
line = ""
if user.engine_id:
line = "-e '{}' ".format(user.engine_id)
line += "'{}' '{}' '{}' '{}' '{}'".format(
_escape_char("'", user.username),
_escape_char("'", user.authentication_type),
_escape_char("'", user.authentication_passphrase),
_escape_char("'", user.privacy_protocol),
_escape_char("'", user.privacy_passphrase),
)
lib.usm_parse_create_usmUser("createUser", line)
if user.engine:
line = "-e '{}'".format(user.engine)
if user.name:
line += " '{}'".format(
_escape_char("'", user.name),
)
if user.auth:
line += " '{}' '{}'".format(
_escape_char("'", user.auth.protocol.name),
_escape_char("'", user.auth.passphrase),
)
if user.priv:
line += " '{}' '{}'".format(
_escape_char("'", user.priv.protocol.name),
_escape_char("'", user.priv.passphrase),
)
lib.usm_parse_create_usmUser("createUser", line.strip())
self._log.debug("create_users: created user: %s", user)
except Exception as e:
self._log.debug(
Expand Down
59 changes: 54 additions & 5 deletions pynetsnmp/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ def __init__(self, name, auth=None, priv=None, engine=None, context=None):

def getArguments(self):
auth = (
("-a", str(self.auth.protocol), "-A", self.auth.passphrase)
("-a", self.auth.protocol.name, "-A", self.auth.passphrase)
if self.auth
else ()
)
if auth:
# The privacy arguments are only given if the authentication
# arguments are also provided.
priv = (
("-x", str(self.priv.protocol), "-X", self.priv.passphrase)
("-x", self.priv.protocol.name, "-X", self.priv.passphrase)
if self.priv
else ()
)
Expand All @@ -69,6 +69,31 @@ def getArguments(self):
+ (("-n", self.context) if self.context else ())
)

def __eq__(self, other):
return (
self.name == other.name
and self.auth == other.auth
and self.priv == other.priv
and self.engine == other.engine
and self.context == other.context
)

def __str__(self):
info = ", ".join(
"{0}={1}".format(k, v)
for k, v in (
("name", self.name),
("auth", self.auth),
("priv", self.priv),
("engine", self.engine),
("context", self.context),
)
if v
)
return "{0.__class__.__name__}(version={0.version}{1}{2})".format(
self, ", " if info else "", info
)


_sec_level = {(True, True): "authPriv", (True, False): "authNoPriv"}
_version_map = {
Expand All @@ -86,28 +111,52 @@ class Authentication(object):
Provides the authentication data for UsmUser objects.
"""

__slots__ = ("protocol", "passphrase")

def __init__(self, protocol, passphrase):
if protocol is None:
raise ValueError(
"Invalid Authentication protocol '{}'".format(protocol)
)
self.protocol = auth_protocols[protocol]
if not passphrase:
raise ValueError(
"Authentication protocol requires a passphrase"
)
raise ValueError("Authentication protocol requires a passphrase")
self.passphrase = passphrase

def __eq__(self, other):
if not isinstance(other, Authentication):
return NotImplemented
return (
self.protocol == other.protocol
and self.passphrase == other.passphrase
)

def __str__(self):
return "{0.__class__.__name__}(protocol={0.protocol})".format(self)


class Privacy(object):
"""
Provides the privacy data for UsmUser objects.
"""

__slots__ = ("protocol", "passphrase")

def __init__(self, protocol, passphrase):
if protocol is None:
raise ValueError("Invalid Privacy protocol '{}'".format(protocol))
self.protocol = priv_protocols[protocol]
if not passphrase:
raise ValueError("Privacy protocol requires a passphrase")
self.passphrase = passphrase

def __eq__(self, other):
if not isinstance(other, Privacy):
return NotImplemented
return (
self.protocol == other.protocol
and self.passphrase == other.passphrase
)

def __str__(self):
return "{0.__class__.__name__}(protocol={0.protocol})".format(self)
17 changes: 13 additions & 4 deletions pynetsnmp/usm.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
from __future__ import absolute_import

class _Protocol(object):
__slots__ = ("__name",)
""" """

__slots__ = ("name",)

def __init__(self, name):
self.__name = name
self.name = name

def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
return self.name == other.name

def __str__(self):
return self.__name
return self.name

def __repr__(self):
return "<{0.__module__}.{0.__name__} {1}>".format(
self.__class__, self.__name
self.__class__, self.name
)


Expand Down

0 comments on commit a3d527c

Please sign in to comment.