diff --git a/plugins/python/example_conversation.py b/plugins/python/example_conversation.py index 3c16702d3..3414fbe30 100644 --- a/plugins/python/example_conversation.py +++ b/plugins/python/example_conversation.py @@ -2,6 +2,7 @@ import sudo import signal from os import path + class ReasonLoggerIOPlugin(sudo.Plugin): """ An example sudo plugin demonstrating how to use the sudo conversation API. @@ -13,9 +14,10 @@ class ReasonLoggerIOPlugin(sudo.Plugin): sudo.ConvMessage has the following fields (see help(sudo.ConvMessage)): msg_type: int Specifies the type of the conversation. See sudo.CONV.* constants below. - timeout: int The maximum amount of time for the conversation in seconds. - After the timeout exceeds, the "sudo.conv" function will - raise sudo.ConversationInterrupted exception. + timeout: int The maximum amount of time for the conversation + in seconds. After the timeout exceeds, the "sudo.conv" + function will raise sudo.ConversationInterrupted + exception. msg: str The message to display for the user. To specify the conversion type you can use the following constants: @@ -27,17 +29,24 @@ class ReasonLoggerIOPlugin(sudo.Plugin): sudo.CONV.PROMPT_ECHO_OK sudo.CONV.PREFER_TTY """ + def open(self, argv, command_info): try: conv_timeout = 120 # in seconds - sudo.log_info("Please provide your reason for executing {}".format(argv)) + sudo.log_info("Please provide your reason " + "for executing {}".format(argv)) - # We ask two questions, the second is not visible on screen, so the user - # can hide a hidden message in case of criminals are forcing him for - # running the command. - # You can either specify the arguments in strict order (timeout being optional), or use named arguments. - message1 = sudo.ConvMessage(sudo.CONV.PROMPT_ECHO_ON, "Reason: ", conv_timeout) - message2 = sudo.ConvMessage(msg="Secret reason: ", timeout=conv_timeout, msg_type=sudo.CONV.PROMPT_MASK) + # We ask two questions, the second is not visible on screen, + # so the user can hide a hidden message in case of criminals are + # forcing him for running the command. + # You can either specify the arguments in strict order (timeout + # being optional), or use named arguments. + message1 = sudo.ConvMessage(sudo.CONV.PROMPT_ECHO_ON, + "Reason: ", + conv_timeout) + message2 = sudo.ConvMessage(msg="Secret reason: ", + timeout=conv_timeout, + msg_type=sudo.CONV.PROMPT_MASK) reply1, reply2 = sudo.conv(message1, message2, on_suspend=self.on_conversation_suspend, on_resume=self.on_conversation_resume) @@ -52,14 +61,18 @@ class ReasonLoggerIOPlugin(sudo.Plugin): return sudo.RC.REJECT def on_conversation_suspend(self, signum): - # This is just an example of how to do something on conversation suspend. - # You can skip specifying 'on_suspend' argument if there is no need - sudo.log_info("conversation suspend: signal", self._signal_name(signum)) + # This is just an example of how to do something on conversation + # suspend. You can skip specifying 'on_suspend' argument if there + # is no need + sudo.log_info("conversation suspend: signal", + self._signal_name(signum)) def on_conversation_resume(self, signum): - # This is just an example of how to do something on conversation resume. - # You can skip specifying 'on_resume' argument if there is no need - sudo.log_info("conversation resume: signal was", self._signal_name(signum)) + # This is just an example of how to do something on conversation + # resume. You can skip specifying 'on_resume' argument if there + # is no need + sudo.log_info("conversation resume: signal was", + self._signal_name(signum)) # helper functions: @classmethod @@ -70,5 +83,6 @@ class ReasonLoggerIOPlugin(sudo.Plugin): return "{}".format(signum) def _log_file_path(self): - log_path = sudo.options_as_dict(self.plugin_options).get("LogPath", "/tmp") + options_dict = sudo.options_as_dict(self.plugin_options) + log_path = options_dict.get("LogPath", "/tmp") return path.join(log_path, "sudo_reasons.txt") diff --git a/plugins/python/example_debugging.py b/plugins/python/example_debugging.py index 221db17b1..a43d34ca1 100644 --- a/plugins/python/example_debugging.py +++ b/plugins/python/example_debugging.py @@ -1,27 +1,35 @@ import sudo + class DebugDemoPlugin(sudo.Plugin): """ An example sudo plugin demonstrating the debugging capabilities. - You can install it as an extra IO plugin for example by adding the following line to sudo.conf: - Plugin python_io python_plugin.so ModulePath=/example_debugging.py ClassName=DebugDemoPlugin + You can install it as an extra IO plugin for example by adding the + following line to sudo.conf: + Plugin python_io python_plugin.so \ + ModulePath=/example_debugging.py \ + ClassName=DebugDemoPlugin To see the plugin's debug output, use the following line in sudo.conf: - Debug python_plugin.so /var/log/sudo_python_debug plugin@trace,c_calls@trace - ^ ^-- the options for the logging - ^----- the output will be placed here + Debug python_plugin.so \ + /var/log/sudo_python_debug plugin@trace,c_calls@trace + ^ ^-- the options for the logging + ^----- the output will be placed here - The options for the logging is in format of multiple "subsystem@level" separated by commas (","). + The options for the logging is in format of multiple "subsystem@level" + separated by commas (","). The most interesting subsystems are: plugin Shows each call of sudo.debug API in the log - - py_calls Logs whenever a C function calls into the python module. (For example calling this __init__ function.) + - py_calls Logs whenever a C function calls into the python module. + (For example calling this __init__ function.) c_calls Logs whenever python calls into a C sudo API function - You can also specify "all" as subsystem name to get the debug messages of all subsystems. + You can also specify "all" as subsystem name to get the debug messages of + all subsystems. Other subsystems available: - internal logs internal functions of the python language wrapper plugin + internal logs internal functions of the python language wrapper sudo_cb logs when sudo calls into its plugin API load logs python plugin loading / unloading @@ -38,21 +46,29 @@ class DebugDemoPlugin(sudo.Plugin): See the sudo.conf manual for more details ("man sudo.conf"). """ + def __init__(self, plugin_options, **kwargs): - # Specify: "py_calls@info" debug option to show the call to this constructor and the arguments passed in + # Specify: "py_calls@info" debug option to show the call to this + # constructor and the arguments passed in - # Specifying "plugin@err" debug option will show this message (or any more verbose level) - sudo.debug(sudo.DEBUG.ERROR, "My demo purpose plugin shows this ERROR level debug message") + # Specifying "plugin@err" debug option will show this message + # (or any more verbose level) + sudo.debug(sudo.DEBUG.ERROR, "My demo purpose plugin shows " + "this ERROR level debug message") - # Specifying "plugin@info" debug option will show this message (or any more verbose level) - sudo.debug(sudo.DEBUG.INFO, "My demo purpose plugin shows this INFO level debug message") + # Specifying "plugin@info" debug option will show this message + # (or any more verbose level) + sudo.debug(sudo.DEBUG.INFO, "My demo purpose plugin shows " + "this INFO level debug message") - # If you raise the level to info or below, the call of the debug will also be logged. + # If you raise the level to info or below, the call of the debug + # will also be logged. # An example output you will see in the debug log file: # Dec 5 15:19:19 sudo[123040] __init__ @ /.../example_debugging.py:54 debugs: # Dec 5 15:19:19 sudo[123040] My demo purpose plugin shows this ERROR level debug message - # Specify: "c_calls@diag" debug option to show this call and its arguments - # If you specify info debug level instead ("c_calls@info"), - # you will also see the python function and line from which you called the 'options_as_dict' function. + # Specify: "c_calls@diag" debug option to show this call and its + # arguments. If you specify info debug level instead ("c_calls@info"), + # you will also see the python function and line from which you called + # the 'options_as_dict' function. self.plugin_options = sudo.options_as_dict(plugin_options) diff --git a/plugins/python/example_group_plugin.py b/plugins/python/example_group_plugin.py index d9c62c0ca..c7d4b5234 100644 --- a/plugins/python/example_group_plugin.py +++ b/plugins/python/example_group_plugin.py @@ -1,7 +1,6 @@ import sudo from typing import Tuple -import pwd class SudoGroupPlugin(sudo.Plugin): @@ -21,9 +20,9 @@ class SudoGroupPlugin(sudo.Plugin): sudo.RC.USAGE_ERROR -2 If the function returns "None" (for example does not call return), it will - be considered sudo.RC.OK. If an exception is raised, its backtrace will be - shown to the user and the plugin function returns sudo.RC.ERROR. If that is - not acceptable, catch it. + be considered sudo.RC.OK. If an exception other than sudo.PluginError is + raised, its backtrace will be shown to the user and the plugin function + returns sudo.RC.ERROR. If that is not acceptable, catch it. """ # -- Plugin API functions -- @@ -34,8 +33,8 @@ class SudoGroupPlugin(sudo.Plugin): database. Otherwise it is a tuple convertible to pwd.struct_passwd. """ hardcoded_user_groups = { - "testgroup": [ "testuser1", "testuser2" ], - "mygroup": [ "test" ] + "testgroup": ["testuser1", "testuser2"], + "mygroup": ["test"] } group_has_user = user in hardcoded_user_groups.get(group, []) diff --git a/plugins/python/example_io_plugin.py b/plugins/python/example_io_plugin.py index 6aa14ab0d..7fbfd41cb 100644 --- a/plugins/python/example_io_plugin.py +++ b/plugins/python/example_io_plugin.py @@ -5,7 +5,7 @@ import errno import signal import sys import json -from typing import Tuple, Dict +from typing import Tuple VERSION = 1.0 @@ -30,14 +30,15 @@ class SudoIOPlugin(sudo.Plugin): sudo.RC.USAGE_ERROR -2 If the function returns "None" (for example does not call return), it will - be considered sudo.RC.OK. If an exception is raised, its backtrace will be - shown to the user and the plugin function returns sudo.RC.ERROR. If that is - not acceptable, catch it. + be considered sudo.RC.OK. If an exception other than sudo.PluginError is + raised, its backtrace will be shown to the user and the plugin function + returns sudo.RC.ERROR. If that is not acceptable, catch it. """ # -- Plugin API functions -- - def __init__(self, version: str, plugin_options: Tuple[str, ...], **kwargs): + def __init__(self, version: str, + plugin_options: Tuple[str, ...], **kwargs): """The constructor of the IO plugin. Other variables you can currently use as arguments are: @@ -65,7 +66,8 @@ class SudoIOPlugin(sudo.Plugin): self._log("", "-- Plugin DESTROYED --") self._log_file.close() - def open(self, argv: Tuple[str, ...], command_info: Tuple[str, ...]) -> int: + def open(self, argv: Tuple[str, ...], + command_info: Tuple[str, ...]) -> int: """Receives the command the user wishes to run. This function works the same as open() call of the C IO plugin API (see diff --git a/plugins/python/example_policy_plugin.py b/plugins/python/example_policy_plugin.py index 33db66f48..d9cbcbc6c 100644 --- a/plugins/python/example_policy_plugin.py +++ b/plugins/python/example_policy_plugin.py @@ -6,8 +6,7 @@ import os import pwd import grp import shutil -from copy import copy -from typing import Tuple, Dict +from typing import Tuple VERSION = 1.0 @@ -36,9 +35,9 @@ class SudoPolicyPlugin(sudo.Plugin): sudo.RC.USAGE_ERROR -2 If the function returns "None" (for example does not call return), it will - be considered sudo.RC.OK. If an exception is raised, its backtrace will be - shown to the user and the plugin function returns sudo.RC.ERROR. If that is - not acceptable, catch it. + be considered sudo.RC.OK. If an exception other than sudo.PluginError is + raised, its backtrace will be shown to the user and the plugin function + returns sudo.RC.ERROR. If that is not acceptable, catch it. """ _allowed_commands = ("id", "whoami") @@ -99,7 +98,7 @@ class SudoPolicyPlugin(sudo.Plugin): # This is how you change the user_env: return (sudo.RC.OK, user_env + ("PLUGIN_EXAMPLE_ENV=1",)) - # If you do not want to change user_env, you can also just return (or None): + # If you do not want to change user_env, you can just return (or None): # return sudo.RC.OK def list(self, argv: Tuple[str, ...], is_verbose: int, user: str): @@ -122,7 +121,8 @@ class SudoPolicyPlugin(sudo.Plugin): pass # we have no cache def show_version(self, is_verbose: int): - sudo.log_info("Python Example Policy Plugin version: {}".format(VERSION)) + sudo.log_info("Python Example Policy Plugin " + "version: {}".format(VERSION)) if is_verbose: sudo.log_info("Python interpreter version:", sys.version) @@ -148,7 +148,7 @@ class SudoPolicyPlugin(sudo.Plugin): path = self.user_env.get("PATH", "/usr/bin:/bin") absolute_cmd = shutil.which(cmd, path=path) if not absolute_cmd: - raise SudoPluginError("Can not find cmd '{}' on PATH".format(cmd)) + raise sudo.PluginError("Can not find cmd '{}' on PATH".format(cmd)) return absolute_cmd def _runas_pwd(self): diff --git a/plugins/python/regress/testdata/check_example_debugging_c_calls@info.log b/plugins/python/regress/testdata/check_example_debugging_c_calls@info.log index ddc6575d7..a0ed8fabe 100644 --- a/plugins/python/regress/testdata/check_example_debugging_c_calls@info.log +++ b/plugins/python/regress/testdata/check_example_debugging_c_calls@info.log @@ -1,7 +1,7 @@ -__init__ @ SRC_DIR/example_debugging.py:45 calls C function: +__init__ @ SRC_DIR/example_debugging.py:56 calls C function: sudo.debug was called with arguments: (, 'My demo purpose plugin shows this ERROR level debug message') -__init__ @ SRC_DIR/example_debugging.py:48 calls C function: +__init__ @ SRC_DIR/example_debugging.py:61 calls C function: sudo.debug was called with arguments: (, 'My demo purpose plugin shows this INFO level debug message') -__init__ @ SRC_DIR/example_debugging.py:58 calls C function: +__init__ @ SRC_DIR/example_debugging.py:74 calls C function: sudo.options_as_dict was called with arguments: (('ModulePath=SRC_DIR/example_debugging.py', 'ClassName=DebugDemoPlugin'),) sudo.options_as_dict returned result: {'ModulePath': 'SRC_DIR/example_debugging.py', 'ClassName': 'DebugDemoPlugin'} diff --git a/plugins/python/regress/testdata/check_example_debugging_plugin@info.log b/plugins/python/regress/testdata/check_example_debugging_plugin@info.log index 0a540a4ba..f8f918edf 100644 --- a/plugins/python/regress/testdata/check_example_debugging_plugin@info.log +++ b/plugins/python/regress/testdata/check_example_debugging_plugin@info.log @@ -1,4 +1,4 @@ -__init__ @ SRC_DIR/example_debugging.py:45 debugs: +__init__ @ SRC_DIR/example_debugging.py:56 debugs: My demo purpose plugin shows this ERROR level debug message -__init__ @ SRC_DIR/example_debugging.py:48 debugs: +__init__ @ SRC_DIR/example_debugging.py:61 debugs: My demo purpose plugin shows this INFO level debug message diff --git a/plugins/python/regress/testdata/check_example_io_plugin_fails_with_python_backtrace.stdout b/plugins/python/regress/testdata/check_example_io_plugin_fails_with_python_backtrace.stdout index 4bdfc6a7a..1fe479cf8 100644 --- a/plugins/python/regress/testdata/check_example_io_plugin_fails_with_python_backtrace.stdout +++ b/plugins/python/regress/testdata/check_example_io_plugin_fails_with_python_backtrace.stdout @@ -1,7 +1,7 @@ Example sudo python plugin will log to /some/not/writable/directory/sudo.log Traceback: - File "SRC_DIR/example_io_plugin.py", line 60, in __init__ + File "SRC_DIR/example_io_plugin.py", line 61, in __init__ self._open_log_file(path.join(log_path, "sudo.log")) - File "SRC_DIR/example_io_plugin.py", line 132, in _open_log_file + File "SRC_DIR/example_io_plugin.py", line 134, in _open_log_file self._log_file = open(log_path, "a")