diff options
Diffstat (limited to 'plugins/python/example_conversation.py')
-rw-r--r-- | plugins/python/example_conversation.py | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/plugins/python/example_conversation.py b/plugins/python/example_conversation.py new file mode 100644 index 0000000..8e68d36 --- /dev/null +++ b/plugins/python/example_conversation.py @@ -0,0 +1,98 @@ +import sudo +import signal +from os import path + + +class ReasonLoggerIOPlugin(sudo.Plugin): + """ + An example sudo plugin demonstrating how to use the sudo conversation API. + + From the python plugin, you can ask something from the user using the + "sudo.conv" function. It expects one or more "sudo.ConvMessage" instances + which specifies how the interaction has to look like. + + 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. + msg: str The message to display for the user. + + To specify the conversion type you can use the following constants: + sudo.CONV.PROMPT_ECHO_OFF + sudo.CONV.PROMPT_ECHO_ON + sudo.CONV.ERROR_MSG + sudo.CONV.INFO_MSG + sudo.CONV.PROMPT_MASK + 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)) + + # 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) + + with open(self._log_file_path(), "a") as file: + print("Executed", ' '.join(argv), file=file) + print("Reason:", reply1, file=file) + print("Hidden reason:", reply2, file=file) + + except sudo.ConversationInterrupted: + sudo.log_error("You did not answer in time") + 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)) + + 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)) + + # helper functions: + if hasattr(signal, "Signals"): + @classmethod + def _signal_name(cls, signum: int): + try: + return signal.Signals(signum).name + except Exception: + return "{}".format(signum) + else: + @classmethod + def _signal_name(cls, signum: int): + for n, v in sorted(signal.__dict__.items()): + if v != signum: + continue + if n.startswith("SIG") and not n.startswith("SIG_"): + return n + return "{}".format(signum) + + def _log_file_path(self): + options_dict = sudo.options_as_dict(self.plugin_options) + log_path = options_dict.get("LogPath", "/tmp") + return path.join(log_path, "sudo_reasons.txt") |