Browse Source

Allow SASL methods to be configured, add EXTERNAL

tags/v0.7.0
Chris Smith 5 years ago
parent
commit
75c3e93343

+ 3
- 0
CHANGELOG View File

@@ -4,6 +4,9 @@ vNEXT (in development)
4 4
  * IrcClients are now constructed using a DSL
5 5
    * Users of the library no longer need to care about the implementing class
6 6
    * Facilitates adding more options in the future without breaking existing implementations
7
+ * SASL improvements
8
+   * The enabled mechanisms can now be configured (in the SASL DSL)
9
+   * Added support for EXTERNAL mechanism, disabled by default
7 10
  * (Internal) Minor version updates for Gradle, Kotlin and JUnit
8 11
 
9 12
 v0.6.0

+ 2
- 2
README.md View File

@@ -80,8 +80,8 @@ client.connect()
80 80
 ### `java.lang.IllegalStateException: Check failed` when connecting to some servers
81 81
 
82 82
 This happens when the IRC server requests an optional client certificate (for use
83
-in SASL auth, usually). At present there is no support for client certificates in
84
-the networking library used by KtIrc. This is fixed in the
83
+in SASL EXTERNAL auth, usually). At present there is no support for client
84
+certificates in the networking library used by KtIrc. This is fixed in the
85 85
 [upstream library](https://github.com/ktorio/ktor/issues/641) and will be included
86 86
 as soon as snapshot builds are available. There is no workaround other than using
87 87
 an insecure connection.

+ 33
- 1
src/main/kotlin/com/dmdirc/ktirc/Dsl.kt View File

@@ -28,6 +28,9 @@ internal data class IrcClientConfig(val server: ServerConfig, val profile: Profi
28 28
  * }
29 29
  *
30 30
  * sasl {
31
+ *     mechanisms += "PLAIN" // or to set the list from scratch:
32
+ *     mechanisms("PLAIN")
33
+ *
31 34
  *     username = "botaccount"
32 35
  *     password = "s3cur3"
33 36
  * }
@@ -54,7 +57,7 @@ class IrcClientConfigBuilder {
54 57
     /**
55 58
      * Configures the profile of the IrcClient user.
56 59
      *
57
-     * At a minimum, [ProfileConfig.nickName] must be supplied.
60
+     * At a minimum, [ProfileConfig.nickname] must be supplied.
58 61
      */
59 62
     @IrcClientDsl
60 63
     fun profile(block: ProfileConfig.() -> Unit) {
@@ -109,11 +112,40 @@ class ProfileConfig {
109 112
 
110 113
 /**
111 114
  * Dsl for configuring SASL authentication.
115
+ *
116
+ * By default the `PLAIN` method will be enabled if SASL is configured.
117
+ *
118
+ * You can modify the mechanisms either by editing the [mechanisms] collection:
119
+ *
120
+ * ```
121
+ * mechanisms += "EXTERNAL"
122
+ * mechanisms.remove("PLAIN")
123
+ * ```
124
+ *
125
+ * or by calling the [mechanisms] function with all the mechanisms you wish
126
+ * to enable:
127
+ *
128
+ * ```
129
+ * mechanisms("PLAIN", "EXTERNAL")
130
+ * ```
131
+ *
132
+ * Priority of mechanisms is determined by KtIrc, regardless of the order
133
+ * they are specified in here.
112 134
  */
113 135
 @IrcClientDsl
114 136
 class SaslConfig {
137
+    /** The SASL mechanisms to enable. */
138
+    val mechanisms: MutableCollection<String> = mutableSetOf("PLAIN")
115 139
     /** The username to provide when authenticating using SASL. */
116 140
     var username: String = ""
117 141
     /** The username to provide when authenticating using SASL. */
118 142
     var password: String = ""
143
+
144
+    @IrcClientDsl
145
+    fun mechanisms(vararg methods: String) {
146
+        with (this.mechanisms) {
147
+            clear()
148
+            addAll(methods)
149
+        }
150
+    }
119 151
 }

+ 1
- 13
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt View File

@@ -4,8 +4,6 @@ import com.dmdirc.ktirc.events.*
4 4
 import com.dmdirc.ktirc.io.*
5 5
 import com.dmdirc.ktirc.messages.*
6 6
 import com.dmdirc.ktirc.model.*
7
-import com.dmdirc.ktirc.sasl.PlainMechanism
8
-import com.dmdirc.ktirc.sasl.SaslMechanism
9 7
 import com.dmdirc.ktirc.util.currentTimeProvider
10 8
 import com.dmdirc.ktirc.util.logger
11 9
 import io.ktor.util.KtorExperimentalAPI
@@ -21,7 +19,6 @@ interface IrcClient {
21 19
     val serverState: ServerState
22 20
     val channelState: ChannelStateMap
23 21
     val userState: UserState
24
-    val hasSaslConfig: Boolean
25 22
 
26 23
     val caseMapping: CaseMapping
27 24
         get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
@@ -107,10 +104,9 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : IrcClient, C
107 104
     @KtorExperimentalAPI
108 105
     internal var socketFactory: (CoroutineScope, String, Int, Boolean) -> LineBufferedSocket = ::KtorLineBufferedSocket
109 106
 
110
-    override val serverState = ServerState(config.profile.nickname, config.server.host, getSaslMechanisms())
107
+    override val serverState = ServerState(config.profile.nickname, config.server.host, config.sasl)
111 108
     override val channelState = ChannelStateMap { caseMapping }
112 109
     override val userState = UserState { caseMapping }
113
-    override val hasSaslConfig = config.sasl != null
114 110
 
115 111
     private val messageHandler = MessageHandler(messageProcessors.toList(), eventHandlers.toMutableList())
116 112
 
@@ -171,12 +167,4 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : IrcClient, C
171 167
         connecting.set(false)
172 168
     }
173 169
 
174
-    private fun getSaslMechanisms(): Collection<SaslMechanism> {
175
-        // TODO: Move this somewhere else
176
-        // TODO: Allow mechanisms to be configured
177
-        config.sasl?.let {
178
-            return listOf(PlainMechanism(it))
179
-        } ?: return emptyList()
180
-    }
181
-
182 170
 }

+ 2
- 2
src/main/kotlin/com/dmdirc/ktirc/events/CapabilitiesHandler.kt View File

@@ -52,7 +52,7 @@ internal class CapabilitiesHandler : EventHandler {
52 52
             log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
53 53
             enabledCapabilities.putAll(capabilities)
54 54
 
55
-            if (client.hasSaslConfig) {
55
+            if (client.serverState.sasl.mechanisms.isNotEmpty()) {
56 56
                 client.serverState.sasl.getPreferredSaslMechanism(enabledCapabilities[Capability.SaslAuthentication])?.let { mechanism ->
57 57
                     log.info { "Attempting SASL authentication using ${mechanism.ircName}" }
58 58
                     client.serverState.sasl.currentMechanism = mechanism
@@ -60,7 +60,7 @@ internal class CapabilitiesHandler : EventHandler {
60 60
                     client.sendAuthenticationMessage(mechanism.ircName)
61 61
                     return
62 62
                 }
63
-                log.warning { "User supplied credentials but we couldn't negotiate a SASL mechanism with the server" }
63
+                log.warning { "SASL is enabled but we couldn't negotiate a SASL mechanism with the server" }
64 64
             }
65 65
 
66 66
             client.endNegotiation()

+ 6
- 1
src/main/kotlin/com/dmdirc/ktirc/model/SaslState.kt View File

@@ -1,10 +1,15 @@
1 1
 package com.dmdirc.ktirc.model
2 2
 
3
+import com.dmdirc.ktirc.SaslConfig
3 4
 import com.dmdirc.ktirc.sasl.SaslMechanism
5
+import com.dmdirc.ktirc.sasl.createSaslMechanism
4 6
 
5
-internal class SaslState(private val mechanisms: Collection<SaslMechanism>) {
7
+internal class SaslState(config: SaslConfig?) {
8
+
9
+    val mechanisms = (config?.createSaslMechanism() ?: emptyList()).toMutableList()
6 10
 
7 11
     var saslBuffer: String = ""
12
+
8 13
     var currentMechanism: SaslMechanism? = null
9 14
         set(value) {
10 15
             mechanismState = null

+ 3
- 3
src/main/kotlin/com/dmdirc/ktirc/model/ServerState.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.model
2 2
 
3
+import com.dmdirc.ktirc.SaslConfig
3 4
 import com.dmdirc.ktirc.io.CaseMapping
4
-import com.dmdirc.ktirc.sasl.SaslMechanism
5 5
 import com.dmdirc.ktirc.util.logger
6 6
 import kotlin.reflect.KClass
7 7
 
@@ -11,7 +11,7 @@ import kotlin.reflect.KClass
11 11
 class ServerState internal constructor(
12 12
         private val initialNickname: String,
13 13
         private val initialServerName: String,
14
-        saslMechanisms: Collection<SaslMechanism>) {
14
+        saslConfig: SaslConfig? = null) {
15 15
 
16 16
     private val log by logger()
17 17
 
@@ -49,7 +49,7 @@ class ServerState internal constructor(
49 49
     val capabilities = CapabilitiesState()
50 50
 
51 51
     /** The current state of SASL authentication. */
52
-    internal val sasl = SaslState(saslMechanisms)
52
+    internal val sasl = SaslState(saslConfig)
53 53
 
54 54
     /**
55 55
      * Convenience accessor for the [ServerFeature.ModePrefixes] feature, which will always have a value.

+ 15
- 0
src/main/kotlin/com/dmdirc/ktirc/sasl/ExternalMechanism.kt View File

@@ -0,0 +1,15 @@
1
+package com.dmdirc.ktirc.sasl
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.messages.sendAuthenticationMessage
5
+
6
+internal class ExternalMechanism : SaslMechanism {
7
+
8
+    override val ircName = "EXTERNAL"
9
+    override val priority = 100
10
+
11
+    override fun handleAuthenticationEvent(client: IrcClient, data: ByteArray?) {
12
+        client.sendAuthenticationMessage("+")
13
+    }
14
+
15
+}

src/main/kotlin/com/dmdirc/ktirc/sasl/Plain.kt → src/main/kotlin/com/dmdirc/ktirc/sasl/PlainMechanism.kt View File


+ 9
- 0
src/main/kotlin/com/dmdirc/ktirc/sasl/SaslMechanism.kt View File

@@ -1,6 +1,7 @@
1 1
 package com.dmdirc.ktirc.sasl
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.SaslConfig
4 5
 
5 6
 internal interface SaslMechanism {
6 7
 
@@ -10,3 +11,11 @@ internal interface SaslMechanism {
10 11
     fun handleAuthenticationEvent(client: IrcClient, data: ByteArray?)
11 12
 
12 13
 }
14
+
15
+internal fun SaslConfig.createSaslMechanism(): List<SaslMechanism> = mechanisms.mapNotNull {
16
+    when (it.toUpperCase()) {
17
+        "EXTERNAL" -> ExternalMechanism()
18
+        "PLAIN" -> PlainMechanism(this)
19
+        else -> null
20
+    }
21
+}

+ 20
- 0
src/test/kotlin/com/dmdirc/ktirc/DslTest.kt View File

@@ -123,3 +123,23 @@ internal class IrcClientConfigBuilderTest {
123 123
     }
124 124
 
125 125
 }
126
+
127
+internal class SaslConfigTest {
128
+
129
+    @Test
130
+    fun `mechanisms function clears all existing mechanisms`() {
131
+        val config = SaslConfig().apply {
132
+            mechanisms += "TEST"
133
+            mechanisms("FOO", "BAR")
134
+        }
135
+
136
+        assertEquals(setOf("FOO", "BAR"), config.mechanisms)
137
+    }
138
+
139
+    @Test
140
+    fun `defaults to plain mechanism`() {
141
+        val config = SaslConfig()
142
+        assertEquals(setOf("PLAIN"), config.mechanisms)
143
+    }
144
+
145
+}

+ 6
- 6
src/test/kotlin/com/dmdirc/ktirc/events/CapabilitiesHandlerTest.kt View File

@@ -30,7 +30,7 @@ internal class CapabilitiesHandlerTest {
30 30
     }
31 31
 
32 32
     private val handler = CapabilitiesHandler()
33
-    private val serverState = ServerState("", "", listOf(saslMech1, saslMech2, saslMech3))
33
+    private val serverState = ServerState("", "", null)
34 34
     private val ircClient = mock<IrcClient> {
35 35
         on { serverState } doReturn serverState
36 36
     }
@@ -92,7 +92,7 @@ internal class CapabilitiesHandlerTest {
92 92
 
93 93
     @Test
94 94
     fun `sends END when capabilities acknowledged and no sasl state`() {
95
-        whenever(ircClient.hasSaslConfig).thenReturn(true)
95
+        serverState.sasl.mechanisms.addAll(listOf(saslMech1, saslMech2, saslMech3))
96 96
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
97 97
                 Capability.EchoMessages to "",
98 98
                 Capability.HostsInNamesReply to "123"
@@ -103,7 +103,7 @@ internal class CapabilitiesHandlerTest {
103 103
 
104 104
     @Test
105 105
     fun `sends END when capabilities acknowledged and no shared mechanism`() {
106
-        whenever(ircClient.hasSaslConfig).thenReturn(true)
106
+        serverState.sasl.mechanisms.addAll(listOf(saslMech1, saslMech2, saslMech3))
107 107
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
108 108
                 Capability.SaslAuthentication to "fake1,fake2",
109 109
                 Capability.HostsInNamesReply to "123"
@@ -114,7 +114,7 @@ internal class CapabilitiesHandlerTest {
114 114
 
115 115
     @Test
116 116
     fun `sends AUTHENTICATE when capabilities acknowledged with shared mechanism`() {
117
-        whenever(ircClient.hasSaslConfig).thenReturn(true)
117
+        serverState.sasl.mechanisms.addAll(listOf(saslMech1, saslMech2, saslMech3))
118 118
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
119 119
                 Capability.SaslAuthentication to "mech1,fake2",
120 120
                 Capability.HostsInNamesReply to "123"
@@ -125,7 +125,7 @@ internal class CapabilitiesHandlerTest {
125 125
 
126 126
     @Test
127 127
     fun `sets current SASL mechanism when capabilities acknowledged with shared mechanism`() {
128
-        whenever(ircClient.hasSaslConfig).thenReturn(true)
128
+        serverState.sasl.mechanisms.addAll(listOf(saslMech1, saslMech2, saslMech3))
129 129
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
130 130
                 Capability.SaslAuthentication to "mech1,fake2",
131 131
                 Capability.HostsInNamesReply to "123"
@@ -136,7 +136,7 @@ internal class CapabilitiesHandlerTest {
136 136
 
137 137
     @Test
138 138
     fun `updates negotiation state when capabilities acknowledged with shared mechanism`() {
139
-        whenever(ircClient.hasSaslConfig).thenReturn(true)
139
+        serverState.sasl.mechanisms.addAll(listOf(saslMech1, saslMech2, saslMech3))
140 140
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
141 141
                 Capability.SaslAuthentication to "mech1,fake2",
142 142
                 Capability.HostsInNamesReply to "123"

+ 1
- 1
src/test/kotlin/com/dmdirc/ktirc/events/ChannelStateHandlerTest.kt View File

@@ -13,7 +13,7 @@ internal class ChannelStateHandlerTest {
13 13
 
14 14
     private val handler = ChannelStateHandler()
15 15
     private val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
16
-    private val serverState = ServerState("", "", emptyList())
16
+    private val serverState = ServerState("", "")
17 17
     private val ircClient = mock<IrcClient> {
18 18
         on { serverState } doReturn serverState
19 19
         on { channelState } doReturn channelStateMap

+ 1
- 1
src/test/kotlin/com/dmdirc/ktirc/events/EventUtilsTest.kt View File

@@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test
16 16
 
17 17
 internal class EventUtilsTest {
18 18
 
19
-    private val serverState = ServerState("", "", emptyList())
19
+    private val serverState = ServerState("", "")
20 20
     private val ircClient = mock<IrcClient> {
21 21
         on { serverState } doReturn serverState
22 22
         on { caseMapping } doReturn CaseMapping.Ascii

+ 1
- 1
src/test/kotlin/com/dmdirc/ktirc/events/ServerStateHandlerTest.kt View File

@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test
11 11
 
12 12
 internal class ServerStateHandlerTest {
13 13
 
14
-    private val serverState = ServerState("", "", emptyList())
14
+    private val serverState = ServerState("", "")
15 15
     private val ircClient = mock<IrcClient> {
16 16
         on { serverState } doReturn serverState
17 17
     }

+ 1
- 1
src/test/kotlin/com/dmdirc/ktirc/events/UserStateHandlerTest.kt View File

@@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test
14 14
 
15 15
 internal class UserStateHandlerTest {
16 16
 
17
-    private val serverState = ServerState("", "", emptyList())
17
+    private val serverState = ServerState("", "")
18 18
     private val userState = UserState { CaseMapping.Rfc }
19 19
 
20 20
     private val ircClient = mock<IrcClient> {

+ 15
- 8
src/test/kotlin/com/dmdirc/ktirc/model/SaslStateTest.kt View File

@@ -27,14 +27,16 @@ internal class SaslStateTest {
27 27
 
28 28
     @Test
29 29
     fun `gets most preferred client SASL mechanism if none are specified by server`() {
30
-        val state = SaslState(mechanisms)
30
+        val state = SaslState(null)
31
+        state.mechanisms.addAll(mechanisms)
31 32
 
32 33
         assertEquals(mech3, state.getPreferredSaslMechanism(""))
33 34
     }
34 35
 
35 36
     @Test
36 37
     fun `gets next preferred client SASL mechanism if one was tried`() {
37
-        val state = SaslState(mechanisms)
38
+        val state = SaslState(null)
39
+        state.mechanisms.addAll(mechanisms)
38 40
         state.currentMechanism = mech3
39 41
 
40 42
         assertEquals(mech2, state.getPreferredSaslMechanism(""))
@@ -42,7 +44,8 @@ internal class SaslStateTest {
42 44
 
43 45
     @Test
44 46
     fun `gets no preferred client SASL mechanism if all were tried`() {
45
-        val state = SaslState(mechanisms)
47
+        val state = SaslState(null)
48
+        state.mechanisms.addAll(mechanisms)
46 49
         state.currentMechanism = mech1
47 50
 
48 51
         assertNull(state.getPreferredSaslMechanism(""))
@@ -50,35 +53,39 @@ internal class SaslStateTest {
50 53
 
51 54
     @Test
52 55
     fun `gets most preferred client SASL mechanism if the server supports all`() {
53
-        val state = SaslState(mechanisms)
56
+        val state = SaslState(null)
57
+        state.mechanisms.addAll(mechanisms)
54 58
 
55 59
         assertEquals(mech3, state.getPreferredSaslMechanism("mech1,mech3,mech2"))
56 60
     }
57 61
 
58 62
     @Test
59 63
     fun `gets most preferred client SASL mechanism if the server supports some`() {
60
-        val state = SaslState(mechanisms)
64
+        val state = SaslState(null)
65
+        state.mechanisms.addAll(mechanisms)
61 66
 
62 67
         assertEquals(mech2, state.getPreferredSaslMechanism("mech2,mech1,other"))
63 68
     }
64 69
 
65 70
     @Test
66 71
     fun `gets no preferred client SASL mechanism if the server supports none`() {
67
-        val state = SaslState(mechanisms)
72
+        val state = SaslState(null)
73
+        state.mechanisms.addAll(mechanisms)
68 74
 
69 75
         assertNull(state.getPreferredSaslMechanism("foo,bar,baz"))
70 76
     }
71 77
 
72 78
     @Test
73 79
     fun `setting the current mechanism clears the existing state`() {
74
-        val state = SaslState(mechanisms)
80
+        val state = SaslState(null)
81
+        state.mechanisms.addAll(mechanisms)
75 82
         state.mechanismState = "in progress"
76 83
         state.currentMechanism = mech2
77 84
         assertNull(state.mechanismState)
78 85
     }
79 86
 
80 87
     @Test
81
-    fun `reset clears all state`() = with(SaslState(mechanisms)) {
88
+    fun `reset clears all state`() = with(SaslState(null)) {
82 89
         currentMechanism = mech2
83 90
         mechanismState = "in progress"
84 91
         saslBuffer = "abcdef"

+ 8
- 8
src/test/kotlin/com/dmdirc/ktirc/model/ServerStateTest.kt View File

@@ -7,25 +7,25 @@ internal class ServerStateTest {
7 7
 
8 8
     @Test
9 9
     fun `ServerState should use the initial nickname as local nickname`() {
10
-        val serverState = ServerState("acidBurn", "", emptyList())
10
+        val serverState = ServerState("acidBurn", "")
11 11
         assertEquals("acidBurn", serverState.localNickname)
12 12
     }
13 13
 
14 14
     @Test
15 15
     fun `ServerState should use the initial name as server name`() {
16
-        val serverState = ServerState("", "the.gibson", emptyList())
16
+        val serverState = ServerState("", "the.gibson")
17 17
         assertEquals("the.gibson", serverState.serverName)
18 18
     }
19 19
 
20 20
     @Test
21 21
     fun `ServerState should default status to disconnected`() {
22
-        val serverState = ServerState("acidBurn", "", emptyList())
22
+        val serverState = ServerState("acidBurn", "")
23 23
         assertEquals(ServerStatus.Disconnected, serverState.status)
24 24
     }
25 25
 
26 26
     @Test
27 27
     fun `returns mode type for known channel mode`() {
28
-        val serverState = ServerState("acidBurn", "", emptyList())
28
+        val serverState = ServerState("acidBurn", "")
29 29
         serverState.features[ServerFeature.ChannelModes] = arrayOf("ab", "cd", "ef", "gh")
30 30
         assertEquals(ChannelModeType.List, serverState.channelModeType('a'))
31 31
         assertEquals(ChannelModeType.SetUnsetParameter, serverState.channelModeType('d'))
@@ -35,7 +35,7 @@ internal class ServerStateTest {
35 35
 
36 36
     @Test
37 37
     fun `returns whether a mode is a channel user mode or not`() {
38
-        val serverState = ServerState("acidBurn", "", emptyList())
38
+        val serverState = ServerState("acidBurn", "")
39 39
         serverState.features[ServerFeature.ModePrefixes] = ModePrefixMapping("oqv", "@~+")
40 40
         assertTrue(serverState.isChannelUserMode('o'))
41 41
         assertTrue(serverState.isChannelUserMode('q'))
@@ -47,19 +47,19 @@ internal class ServerStateTest {
47 47
 
48 48
     @Test
49 49
     fun `returns NoParameter for unknown channel mode`() {
50
-        val serverState = ServerState("acidBurn", "", emptyList())
50
+        val serverState = ServerState("acidBurn", "")
51 51
         serverState.features[ServerFeature.ChannelModes] = arrayOf("ab", "cd", "ef", "gh")
52 52
         assertEquals(ChannelModeType.NoParameter, serverState.channelModeType('z'))
53 53
     }
54 54
 
55 55
     @Test
56 56
     fun `returns NoParameter for channel modes if feature doesn't exist`() {
57
-        val serverState = ServerState("acidBurn", "", emptyList())
57
+        val serverState = ServerState("acidBurn", "")
58 58
         assertEquals(ChannelModeType.NoParameter, serverState.channelModeType('b'))
59 59
     }
60 60
 
61 61
     @Test
62
-    fun `reset clears all state`() = with(ServerState("acidBurn", "", emptyList())) {
62
+    fun `reset clears all state`() = with(ServerState("acidBurn", "")) {
63 63
         receivedWelcome = true
64 64
         status = ServerStatus.Connecting
65 65
         localNickname = "acidBurn3"

+ 20
- 0
src/test/kotlin/com/dmdirc/ktirc/sasl/ExternalMechanismTest.kt View File

@@ -0,0 +1,20 @@
1
+package com.dmdirc.ktirc.sasl
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.nhaarman.mockitokotlin2.mock
5
+import com.nhaarman.mockitokotlin2.verify
6
+import org.junit.jupiter.api.Test
7
+
8
+internal class ExternalMechanismTest {
9
+
10
+    @Test
11
+    fun `sends + when receiving server message`() {
12
+        val mechanism = ExternalMechanism()
13
+        val client = mock<IrcClient>()
14
+
15
+        mechanism.handleAuthenticationEvent(client, null)
16
+
17
+        verify(client).send("AUTHENTICATE +")
18
+    }
19
+
20
+}

+ 1
- 1
src/test/kotlin/com/dmdirc/ktirc/sasl/PlainMechanismTest.kt View File

@@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test
12 12
 
13 13
 internal class PlainMechanismTest {
14 14
 
15
-    private val serverState = ServerState("", "", emptyList())
15
+    private val serverState = ServerState("", "")
16 16
     private val ircClient = mock<IrcClient> {
17 17
         on { serverState } doReturn serverState
18 18
     }

+ 25
- 0
src/test/kotlin/com/dmdirc/ktirc/sasl/SaslMechanismTest.kt View File

@@ -0,0 +1,25 @@
1
+package com.dmdirc.ktirc.sasl
2
+
3
+import com.dmdirc.ktirc.SaslConfig
4
+import org.junit.jupiter.api.Assertions.assertEquals
5
+import org.junit.jupiter.api.Assertions.assertTrue
6
+import org.junit.jupiter.api.Test
7
+
8
+internal class SaslMechanismTest {
9
+
10
+    @Test
11
+    fun `creates sasl mechanisms by name`() {
12
+        val mechanisms = SaslConfig().apply { mechanisms("PLAIN", "EXTERNAL") }.createSaslMechanism()
13
+        assertEquals(2, mechanisms.size)
14
+        assertTrue(mechanisms[0] is PlainMechanism)
15
+        assertTrue(mechanisms[1] is ExternalMechanism)
16
+    }
17
+
18
+    @Test
19
+    fun `ignores unknown sasl mechanisms`() {
20
+        val mechanisms = SaslConfig().apply { mechanisms("PLAIN", "SPICY") }.createSaslMechanism()
21
+        assertEquals(1, mechanisms.size)
22
+        assertTrue(mechanisms[0] is PlainMechanism)
23
+    }
24
+
25
+}

Loading…
Cancel
Save