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
  * IrcClients are now constructed using a DSL
4
  * IrcClients are now constructed using a DSL
5
    * Users of the library no longer need to care about the implementing class
5
    * Users of the library no longer need to care about the implementing class
6
    * Facilitates adding more options in the future without breaking existing implementations
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
  * (Internal) Minor version updates for Gradle, Kotlin and JUnit
10
  * (Internal) Minor version updates for Gradle, Kotlin and JUnit
8
 
11
 
9
 v0.6.0
12
 v0.6.0

+ 2
- 2
README.md View File

80
 ### `java.lang.IllegalStateException: Check failed` when connecting to some servers
80
 ### `java.lang.IllegalStateException: Check failed` when connecting to some servers
81
 
81
 
82
 This happens when the IRC server requests an optional client certificate (for use
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
 [upstream library](https://github.com/ktorio/ktor/issues/641) and will be included
85
 [upstream library](https://github.com/ktorio/ktor/issues/641) and will be included
86
 as soon as snapshot builds are available. There is no workaround other than using
86
 as soon as snapshot builds are available. There is no workaround other than using
87
 an insecure connection.
87
 an insecure connection.

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

28
  * }
28
  * }
29
  *
29
  *
30
  * sasl {
30
  * sasl {
31
+ *     mechanisms += "PLAIN" // or to set the list from scratch:
32
+ *     mechanisms("PLAIN")
33
+ *
31
  *     username = "botaccount"
34
  *     username = "botaccount"
32
  *     password = "s3cur3"
35
  *     password = "s3cur3"
33
  * }
36
  * }
54
     /**
57
     /**
55
      * Configures the profile of the IrcClient user.
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
     @IrcClientDsl
62
     @IrcClientDsl
60
     fun profile(block: ProfileConfig.() -> Unit) {
63
     fun profile(block: ProfileConfig.() -> Unit) {
109
 
112
 
110
 /**
113
 /**
111
  * Dsl for configuring SASL authentication.
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
 @IrcClientDsl
135
 @IrcClientDsl
114
 class SaslConfig {
136
 class SaslConfig {
137
+    /** The SASL mechanisms to enable. */
138
+    val mechanisms: MutableCollection<String> = mutableSetOf("PLAIN")
115
     /** The username to provide when authenticating using SASL. */
139
     /** The username to provide when authenticating using SASL. */
116
     var username: String = ""
140
     var username: String = ""
117
     /** The username to provide when authenticating using SASL. */
141
     /** The username to provide when authenticating using SASL. */
118
     var password: String = ""
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
 import com.dmdirc.ktirc.io.*
4
 import com.dmdirc.ktirc.io.*
5
 import com.dmdirc.ktirc.messages.*
5
 import com.dmdirc.ktirc.messages.*
6
 import com.dmdirc.ktirc.model.*
6
 import com.dmdirc.ktirc.model.*
7
-import com.dmdirc.ktirc.sasl.PlainMechanism
8
-import com.dmdirc.ktirc.sasl.SaslMechanism
9
 import com.dmdirc.ktirc.util.currentTimeProvider
7
 import com.dmdirc.ktirc.util.currentTimeProvider
10
 import com.dmdirc.ktirc.util.logger
8
 import com.dmdirc.ktirc.util.logger
11
 import io.ktor.util.KtorExperimentalAPI
9
 import io.ktor.util.KtorExperimentalAPI
21
     val serverState: ServerState
19
     val serverState: ServerState
22
     val channelState: ChannelStateMap
20
     val channelState: ChannelStateMap
23
     val userState: UserState
21
     val userState: UserState
24
-    val hasSaslConfig: Boolean
25
 
22
 
26
     val caseMapping: CaseMapping
23
     val caseMapping: CaseMapping
27
         get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
24
         get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
107
     @KtorExperimentalAPI
104
     @KtorExperimentalAPI
108
     internal var socketFactory: (CoroutineScope, String, Int, Boolean) -> LineBufferedSocket = ::KtorLineBufferedSocket
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
     override val channelState = ChannelStateMap { caseMapping }
108
     override val channelState = ChannelStateMap { caseMapping }
112
     override val userState = UserState { caseMapping }
109
     override val userState = UserState { caseMapping }
113
-    override val hasSaslConfig = config.sasl != null
114
 
110
 
115
     private val messageHandler = MessageHandler(messageProcessors.toList(), eventHandlers.toMutableList())
111
     private val messageHandler = MessageHandler(messageProcessors.toList(), eventHandlers.toMutableList())
116
 
112
 
171
         connecting.set(false)
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
             log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
52
             log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
53
             enabledCapabilities.putAll(capabilities)
53
             enabledCapabilities.putAll(capabilities)
54
 
54
 
55
-            if (client.hasSaslConfig) {
55
+            if (client.serverState.sasl.mechanisms.isNotEmpty()) {
56
                 client.serverState.sasl.getPreferredSaslMechanism(enabledCapabilities[Capability.SaslAuthentication])?.let { mechanism ->
56
                 client.serverState.sasl.getPreferredSaslMechanism(enabledCapabilities[Capability.SaslAuthentication])?.let { mechanism ->
57
                     log.info { "Attempting SASL authentication using ${mechanism.ircName}" }
57
                     log.info { "Attempting SASL authentication using ${mechanism.ircName}" }
58
                     client.serverState.sasl.currentMechanism = mechanism
58
                     client.serverState.sasl.currentMechanism = mechanism
60
                     client.sendAuthenticationMessage(mechanism.ircName)
60
                     client.sendAuthenticationMessage(mechanism.ircName)
61
                     return
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
             client.endNegotiation()
66
             client.endNegotiation()

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

1
 package com.dmdirc.ktirc.model
1
 package com.dmdirc.ktirc.model
2
 
2
 
3
+import com.dmdirc.ktirc.SaslConfig
3
 import com.dmdirc.ktirc.sasl.SaslMechanism
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
     var saslBuffer: String = ""
11
     var saslBuffer: String = ""
12
+
8
     var currentMechanism: SaslMechanism? = null
13
     var currentMechanism: SaslMechanism? = null
9
         set(value) {
14
         set(value) {
10
             mechanismState = null
15
             mechanismState = null

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

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

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
 package com.dmdirc.ktirc.sasl
1
 package com.dmdirc.ktirc.sasl
2
 
2
 
3
 import com.dmdirc.ktirc.IrcClient
3
 import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.SaslConfig
4
 
5
 
5
 internal interface SaslMechanism {
6
 internal interface SaslMechanism {
6
 
7
 
10
     fun handleAuthenticationEvent(client: IrcClient, data: ByteArray?)
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
     }
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
     }
30
     }
31
 
31
 
32
     private val handler = CapabilitiesHandler()
32
     private val handler = CapabilitiesHandler()
33
-    private val serverState = ServerState("", "", listOf(saslMech1, saslMech2, saslMech3))
33
+    private val serverState = ServerState("", "", null)
34
     private val ircClient = mock<IrcClient> {
34
     private val ircClient = mock<IrcClient> {
35
         on { serverState } doReturn serverState
35
         on { serverState } doReturn serverState
36
     }
36
     }
92
 
92
 
93
     @Test
93
     @Test
94
     fun `sends END when capabilities acknowledged and no sasl state`() {
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
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
96
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
97
                 Capability.EchoMessages to "",
97
                 Capability.EchoMessages to "",
98
                 Capability.HostsInNamesReply to "123"
98
                 Capability.HostsInNamesReply to "123"
103
 
103
 
104
     @Test
104
     @Test
105
     fun `sends END when capabilities acknowledged and no shared mechanism`() {
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
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
107
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
108
                 Capability.SaslAuthentication to "fake1,fake2",
108
                 Capability.SaslAuthentication to "fake1,fake2",
109
                 Capability.HostsInNamesReply to "123"
109
                 Capability.HostsInNamesReply to "123"
114
 
114
 
115
     @Test
115
     @Test
116
     fun `sends AUTHENTICATE when capabilities acknowledged with shared mechanism`() {
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
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
118
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
119
                 Capability.SaslAuthentication to "mech1,fake2",
119
                 Capability.SaslAuthentication to "mech1,fake2",
120
                 Capability.HostsInNamesReply to "123"
120
                 Capability.HostsInNamesReply to "123"
125
 
125
 
126
     @Test
126
     @Test
127
     fun `sets current SASL mechanism when capabilities acknowledged with shared mechanism`() {
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
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
129
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
130
                 Capability.SaslAuthentication to "mech1,fake2",
130
                 Capability.SaslAuthentication to "mech1,fake2",
131
                 Capability.HostsInNamesReply to "123"
131
                 Capability.HostsInNamesReply to "123"
136
 
136
 
137
     @Test
137
     @Test
138
     fun `updates negotiation state when capabilities acknowledged with shared mechanism`() {
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
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
140
         handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(TestConstants.time, hashMapOf(
141
                 Capability.SaslAuthentication to "mech1,fake2",
141
                 Capability.SaslAuthentication to "mech1,fake2",
142
                 Capability.HostsInNamesReply to "123"
142
                 Capability.HostsInNamesReply to "123"

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

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

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

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

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

11
 
11
 
12
 internal class ServerStateHandlerTest {
12
 internal class ServerStateHandlerTest {
13
 
13
 
14
-    private val serverState = ServerState("", "", emptyList())
14
+    private val serverState = ServerState("", "")
15
     private val ircClient = mock<IrcClient> {
15
     private val ircClient = mock<IrcClient> {
16
         on { serverState } doReturn serverState
16
         on { serverState } doReturn serverState
17
     }
17
     }

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

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

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

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

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

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

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

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
 
12
 
13
 internal class PlainMechanismTest {
13
 internal class PlainMechanismTest {
14
 
14
 
15
-    private val serverState = ServerState("", "", emptyList())
15
+    private val serverState = ServerState("", "")
16
     private val ircClient = mock<IrcClient> {
16
     private val ircClient = mock<IrcClient> {
17
         on { serverState } doReturn serverState
17
         on { serverState } doReturn serverState
18
     }
18
     }

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

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