diff --git a/sopel/coretasks.py b/sopel/coretasks.py index e3effb9c3..dbfbdb978 100644 --- a/sopel/coretasks.py +++ b/sopel/coretasks.py @@ -98,8 +98,10 @@ def _handle_sasl_capability( # Manage CAP REQ :sasl auth_method = bot.settings.core.auth_method server_auth_method = bot.settings.core.server_auth_method + auth_target = bot.settings.core.auth_target is_required = 'sasl' in (auth_method, server_auth_method) + LOGGER.critical("FOO. BAR!") if not is_required: # not required: we are fine, available or not return plugin.CapabilityNegotiation.DONE @@ -110,6 +112,12 @@ def _handle_sasl_capability( 'cannot authenticate with SASL.', ) return plugin.CapabilityNegotiation.ERROR + elif auth_target not in ["PLAIN", "EXTERNAL", "SCRAM-SHA-256"]: + LOGGER.error( + 'Configured SASL method %r is not supported by Sopel.', + auth_target, + ) + return plugin.CapabilityNegotiation.ERROR # Check SASL configuration (password is required) password, mech = _get_sasl_pass_and_mech(bot) diff --git a/test/test_coretasks.py b/test/test_coretasks.py index 2d1e98c2b..8ead31f04 100644 --- a/test/test_coretasks.py +++ b/test/test_coretasks.py @@ -4,6 +4,7 @@ from base64 import b64decode, b64encode from datetime import datetime, timezone import logging +from typing import TYPE_CHECKING import pytest from scramp import ScramMechanism @@ -14,6 +15,9 @@ from sopel.tests import rawlist from sopel.tools import Identifier +if TYPE_CHECKING: + from sopel.bot import Sopel + TMP_CONFIG = """ [core] @@ -512,29 +516,47 @@ def test_sasl_plain_token_generation(): 'sopel\x00sopel\x00sasliscool') -def test_sasl_plain_auth(mockbot): +def test_sasl_bad_method(mockbot: Sopel): + """Verify the bot behaves when configured with an unsupported SASL method.""" + mockbot.settings.core.auth_method = "sasl" + mockbot.settings.core.auth_target = "SCRAM-MD4" + mockbot.on_message("CAP * LS :sasl") + mockbot.on_message("CAP TestBot ACK :sasl") + assert mockbot.backend.message_sent == rawlist( + "CAP REQ :sasl", + "CAP END", + ) + #with pytest.raises(Exception): + mockbot.on_message("AUTHENTICATE +") + + +def test_sasl_plain_auth(mockbot: Sopel): """Verify the bot performs SASL PLAIN auth correctly.""" mockbot.settings.core.auth_method = "sasl" mockbot.settings.core.auth_target = "PLAIN" + mockbot.on_message("CAP * LS :sasl") mockbot.on_message("CAP TestBot ACK :sasl") - assert mockbot.backend.message_sent == rawlist("AUTHENTICATE PLAIN") - mockbot.on_message("AUTHENTICATE +") assert mockbot.backend.message_sent == rawlist( + "CAP REQ :sasl", "AUTHENTICATE PLAIN", - "AUTHENTICATE VGVzdEJvdABUZXN0Qm90AGh1bnRlcjI=", + ) + mockbot.on_message("AUTHENTICATE +") + assert ( + len(mockbot.backend.message_sent) == 3 + and mockbot.backend.message_sent[-1] + == rawlist("AUTHENTICATE VGVzdEJvdABUZXN0Qm90AGh1bnRlcjI=")[0] ) mockbot.on_message( "900 TestBot test!test@test TestBot :You are now logged in as TestBot" ) mockbot.on_message("903 TestBot :SASL authentication succeeded") - assert mockbot.backend.message_sent == rawlist( - "AUTHENTICATE PLAIN", - "AUTHENTICATE VGVzdEJvdABUZXN0Qm90AGh1bnRlcjI=", - "CAP END", + assert ( + len(mockbot.backend.message_sent) == 4 + and mockbot.backend.message_sent[-1] == rawlist("CAP END")[0] ) -def test_sasl_scram_sha_256_auth(mockbot): +def test_sasl_scram_sha_256_auth(mockbot: Sopel): """Verify the bot performs SASL SCRAM-SHA-256 auth correctly.""" mech = ScramMechanism() salt, stored_key, server_key, iter_count = mech.make_auth_info( @@ -546,8 +568,12 @@ def test_sasl_scram_sha_256_auth(mockbot): mockbot.settings.core.auth_method = "sasl" mockbot.settings.core.auth_target = "SCRAM-SHA-256" + mockbot.on_message("CAP * LS :sasl") mockbot.on_message("CAP TestBot ACK :sasl") - assert mockbot.backend.message_sent == rawlist("AUTHENTICATE SCRAM-SHA-256") + assert mockbot.backend.message_sent == rawlist( + "CAP REQ :sasl", + "AUTHENTICATE SCRAM-SHA-256", + ) mockbot.on_message("AUTHENTICATE +") scram_server.set_client_first( @@ -565,7 +591,7 @@ def test_sasl_scram_sha_256_auth(mockbot): + b64encode(scram_server.get_server_final().encode("utf-8")).decode("utf-8") ) assert ( - len(mockbot.backend.message_sent) == 4 + len(mockbot.backend.message_sent) == 5 and mockbot.backend.message_sent[-1] == rawlist("AUTHENTICATE +")[0] ) @@ -574,12 +600,12 @@ def test_sasl_scram_sha_256_auth(mockbot): ) mockbot.on_message("903 TestBot :SASL authentication succeeded") assert ( - len(mockbot.backend.message_sent) == 5 + len(mockbot.backend.message_sent) == 6 and mockbot.backend.message_sent[-1] == rawlist("CAP END")[0] ) -def test_sasl_scram_sha_256_nonsense_server_first(mockbot): +def test_sasl_scram_sha_256_nonsense_server_first(mockbot: Sopel): """Verify the bot handles a nonsense SCRAM-SHA-256 server_first correctly.""" mech = ScramMechanism() salt, stored_key, server_key, iter_count = mech.make_auth_info( @@ -591,6 +617,7 @@ def test_sasl_scram_sha_256_nonsense_server_first(mockbot): mockbot.settings.core.auth_method = "sasl" mockbot.settings.core.auth_target = "SCRAM-SHA-256" + mockbot.on_message("CAP * LS :sasl") mockbot.on_message("CAP TestBot ACK :sasl") mockbot.on_message("AUTHENTICATE +") @@ -599,12 +626,12 @@ def test_sasl_scram_sha_256_nonsense_server_first(mockbot): ) mockbot.on_message("AUTHENTICATE " + b64encode(b"junk").decode("utf-8")) assert ( - len(mockbot.backend.message_sent) == 3 + len(mockbot.backend.message_sent) == 4 and mockbot.backend.message_sent[-1] == rawlist("AUTHENTICATE *")[0] ) -def test_sasl_scram_sha_256_nonsense_server_final(mockbot): +def test_sasl_scram_sha_256_nonsense_server_final(mockbot: Sopel): """Verify the bot handles a nonsense SCRAM-SHA-256 server_final correctly.""" mech = ScramMechanism() salt, stored_key, server_key, iter_count = mech.make_auth_info( @@ -616,6 +643,7 @@ def test_sasl_scram_sha_256_nonsense_server_final(mockbot): mockbot.settings.core.auth_method = "sasl" mockbot.settings.core.auth_target = "SCRAM-SHA-256" + mockbot.on_message("CAP * LS :sasl") mockbot.on_message("CAP TestBot ACK :sasl") mockbot.on_message("AUTHENTICATE +") @@ -631,27 +659,28 @@ def test_sasl_scram_sha_256_nonsense_server_final(mockbot): ) mockbot.on_message("AUTHENTICATE " + b64encode(b"junk").decode("utf-8")) assert ( - len(mockbot.backend.message_sent) == 4 + len(mockbot.backend.message_sent) == 5 and mockbot.backend.message_sent[-1] == rawlist("AUTHENTICATE *")[0] ) -def test_sasl_scram_sha_256_error_server_first(mockbot): +def test_sasl_scram_sha_256_error_server_first(mockbot: Sopel): """Verify the bot handles an error SCRAM-SHA-256 server_first correctly.""" mockbot.settings.core.auth_method = "sasl" mockbot.settings.core.auth_target = "SCRAM-SHA-256" + mockbot.on_message("CAP * LS :sasl") mockbot.on_message("CAP TestBot ACK :sasl") mockbot.on_message("AUTHENTICATE +") mockbot.on_message("AUTHENTICATE " + b64encode(b"e=some-error").decode("utf-8")) assert ( - len(mockbot.backend.message_sent) == 3 + len(mockbot.backend.message_sent) == 4 and mockbot.backend.message_sent[-1] == rawlist("AUTHENTICATE *")[0] ) -def test_sasl_scram_sha_256_error_server_final(mockbot): +def test_sasl_scram_sha_256_error_server_final(mockbot: Sopel): """Verify the bot handles an error SCRAM-SHA-256 server_final correctly.""" mech = ScramMechanism() salt, stored_key, server_key, iter_count = mech.make_auth_info( @@ -663,6 +692,7 @@ def test_sasl_scram_sha_256_error_server_final(mockbot): mockbot.settings.core.auth_method = "sasl" mockbot.settings.core.auth_target = "SCRAM-SHA-256" + mockbot.on_message("CAP * LS :sasl") mockbot.on_message("CAP TestBot ACK :sasl") mockbot.on_message("AUTHENTICATE +") @@ -678,7 +708,7 @@ def test_sasl_scram_sha_256_error_server_final(mockbot): ) mockbot.on_message("AUTHENTICATE " + b64encode(b"e=some-error").decode("utf-8")) assert ( - len(mockbot.backend.message_sent) == 4 + len(mockbot.backend.message_sent) == 5 and mockbot.backend.message_sent[-1] == rawlist("AUTHENTICATE *")[0] )