Skip to content

Commit fad0116

Browse files
marcin-cebopubnub-release-botparfeon
authored
Can read unencrypted history message when crypto is configured. (#153)
Co-authored-by: PubNub Release Bot <[email protected]> Co-authored-by: Serhii Mamontov <[email protected]>
1 parent 191f1d2 commit fad0116

File tree

15 files changed

+369
-83
lines changed

15 files changed

+369
-83
lines changed

.pubnub.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name: kotlin
2-
version: 7.7.1
2+
version: 7.7.2
33
schema: 1
44
scm: github.com/pubnub/kotlin
55
files:
6-
- build/libs/pubnub-kotlin-7.7.1-all.jar
6+
- build/libs/pubnub-kotlin-7.7.2-all.jar
77
sdks:
88
-
99
type: library
@@ -23,8 +23,8 @@ sdks:
2323
-
2424
distribution-type: library
2525
distribution-repository: maven
26-
package-name: pubnub-kotlin-7.7.1
27-
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-kotlin/7.7.1/pubnub-kotlin-7.7.1.jar
26+
package-name: pubnub-kotlin-7.7.2
27+
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-kotlin/7.7.2/pubnub-kotlin-7.7.2.jar
2828
supported-platforms:
2929
supported-operating-systems:
3030
Android:
@@ -114,6 +114,11 @@ sdks:
114114
license-url: https://www.apache.org/licenses/LICENSE-2.0.txt
115115
is-required: Required
116116
changelog:
117+
- date: 2023-11-08
118+
version: v7.7.2
119+
changes:
120+
- type: bug
121+
text: "Fixed reading unencrypted history message when crypto is configured."
117122
- date: 2023-10-30
118123
version: v7.7.1
119124
changes:

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## v7.7.2
2+
November 08 2023
3+
4+
#### Fixed
5+
- Fixed reading unencrypted history message when crypto is configured.
6+
7+
8+
19
## v7.7.1
210
October 30 2023
311

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ You will need the publish and subscribe keys to authenticate your app. Get your
2020
<dependency>
2121
<groupId>com.pubnub</groupId>
2222
<artifactId>pubnub-kotlin</artifactId>
23-
<version>7.7.1</version>
23+
<version>7.7.2</version>
2424
</dependency>
2525
```
2626

2727
* for Gradle, add the following dependency in your `gradle.build`:
2828
```groovy
29-
implementation 'com.pubnub:pubnub-kotlin:7.7.1'
29+
implementation 'com.pubnub:pubnub-kotlin:7.7.2'
3030
```
3131

3232
2. Configure your keys:

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ SONATYPE_HOST=DEFAULT
2121
SONATYPE_AUTOMATIC_RELEASE=true
2222
GROUP=com.pubnub
2323
POM_ARTIFACT_ID=pubnub-kotlin
24-
VERSION_NAME=7.7.1
24+
VERSION_NAME=7.7.2
2525
POM_PACKAGING=jar
2626

2727
POM_NAME=PubNub Kotlin SDK

src/integrationTest/kotlin/com/pubnub/api/integration/HistoryIntegrationTest.kt

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import com.google.gson.JsonPrimitive
55
import com.pubnub.api.CommonUtils
66
import com.pubnub.api.CommonUtils.emoji
77
import com.pubnub.api.CommonUtils.randomChannel
8+
import com.pubnub.api.PubNub
9+
import com.pubnub.api.PubNubError
10+
import com.pubnub.api.crypto.CryptoModule
811
import com.pubnub.api.models.consumer.history.Action
912
import com.pubnub.api.models.consumer.history.HistoryMessageType
1013
import com.pubnub.api.models.consumer.history.PNFetchMessageItem
@@ -61,6 +64,56 @@ class HistoryIntegrationTest : BaseIntegrationTest() {
6164
assertEquals(expectedHistoryResultChannels, historyResult?.messages)
6265
}
6366

67+
@Test
68+
fun `when reading unencrypted message from history using pubNub with configured encryption should log error and return error in response and return unencrypted message`() {
69+
val pnConfigurationWithCrypto = getBasicPnConfiguration()
70+
val cipherKey = "enigma"
71+
pnConfigurationWithCrypto.cryptoModule =
72+
CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = false)
73+
val pubNubWithCrypto = PubNub(pnConfigurationWithCrypto)
74+
75+
val channel = randomChannel()
76+
val expectedMeta = JsonObject().also { it.add("thisIsMeta", JsonPrimitive("thisIsMetaValue")) }
77+
val expectedMessage = "this is not encrypted message"
78+
79+
val result = pubnub.publish(
80+
channel = channel,
81+
message = expectedMessage,
82+
meta = expectedMeta,
83+
shouldStore = true,
84+
ttl = 60
85+
).sync()!!
86+
87+
var historyResult: PNHistoryResult? = null
88+
89+
await
90+
.pollInterval(Duration.ofMillis(1_000))
91+
.pollDelay(Duration.ofMillis(1_000))
92+
.atMost(Duration.ofMillis(10_000))
93+
.untilAsserted {
94+
historyResult = pubNubWithCrypto.history(
95+
channel = channel,
96+
includeMeta = true,
97+
includeTimetoken = true,
98+
).sync()
99+
100+
assertNotNull(historyResult)
101+
assertThat(historyResult?.messages, hasSize(not(0)))
102+
}
103+
104+
val expectedHistoryResultChannels =
105+
listOf(
106+
PNHistoryItemResult(
107+
entry = JsonPrimitive(expectedMessage),
108+
timetoken = result.timetoken,
109+
meta = expectedMeta,
110+
error = PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED
111+
)
112+
)
113+
114+
assertEquals(expectedHistoryResultChannels, historyResult?.messages)
115+
}
116+
64117
@Test
65118
fun fetchMessagesSingleScenario() {
66119
val channel = randomChannel()
@@ -150,6 +203,65 @@ class HistoryIntegrationTest : BaseIntegrationTest() {
150203
)
151204
}
152205

206+
@Test
207+
fun `when fetching unencrypted message using pubNub with configured encryption should log error and return error in respone and return unencrypted message`() {
208+
val pnConfigurationWithCrypto = getBasicPnConfiguration()
209+
val cipherKey = "enigma"
210+
pnConfigurationWithCrypto.cryptoModule =
211+
CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true)
212+
val pubNubWithCrypto = PubNub(pnConfigurationWithCrypto)
213+
214+
val channel = randomChannel()
215+
val expectedMeta = JsonObject().also { it.add("thisIsMeta", JsonPrimitive("thisIsMetaValue")) }
216+
val expectedMessage = CommonUtils.generatePayload()
217+
218+
val result = pubnub.publish(
219+
channel = channel,
220+
message = expectedMessage,
221+
meta = expectedMeta,
222+
shouldStore = true,
223+
ttl = 60
224+
).sync()!!
225+
226+
var fetchResult: PNFetchMessagesResult? = null
227+
228+
await
229+
.pollInterval(Duration.ofMillis(1_000))
230+
.pollDelay(Duration.ofMillis(1_000))
231+
.atMost(Duration.ofMillis(10_000))
232+
.untilAsserted {
233+
fetchResult = pubNubWithCrypto.fetchMessages(
234+
includeMeta = true,
235+
includeMessageActions = true,
236+
includeMessageType = true,
237+
includeUUID = true,
238+
channels = listOf(channel)
239+
).sync()
240+
assertNotNull(fetchResult)
241+
assertThat(fetchResult?.channels, aMapWithSize(not(0)))
242+
}
243+
244+
val expectedItem = PNFetchMessageItem(
245+
uuid = pubnub.configuration.userId.value,
246+
message = expectedMessage,
247+
timetoken = result.timetoken,
248+
meta = expectedMeta,
249+
messageType = HistoryMessageType.Message,
250+
actions = emptyMap<String, Map<String, List<Action>>>(),
251+
error = PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED
252+
)
253+
254+
val expectedChannelsResponse: Map<String, List<PNFetchMessageItem>> =
255+
mapOf(
256+
channel to listOf(
257+
expectedItem
258+
)
259+
)
260+
261+
val fetchMessageItem = fetchResult!!
262+
assertEquals(expectedChannelsResponse, fetchMessageItem.channels)
263+
}
264+
153265
@Test
154266
fun always() {
155267
assertEquals(

src/main/kotlin/com/pubnub/api/PubNub.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class PubNub internal constructor(
106106

107107
companion object {
108108
private const val TIMESTAMP_DIVIDER = 1000
109-
private const val SDK_VERSION = "7.7.1"
109+
private const val SDK_VERSION = "7.7.2"
110110
private const val MAX_SEQUENCE = 65535
111111

112112
/**

src/main/kotlin/com/pubnub/api/PubNubError.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ enum class PubNubError(private val code: Int, val message: String) {
226226
"Encryption of empty data not allowed."
227227
),
228228

229+
CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED(
230+
177,
231+
"Crypto is configured but message is not encrypted."
232+
),
233+
229234
;
230235

231236
override fun toString(): String {

src/main/kotlin/com/pubnub/api/endpoints/FetchMessages.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ class FetchMessages internal constructor(
8282
val body = input.body()!!
8383
val channelsMap = body.channels.mapValues { (_, value) ->
8484
value.map { serverMessageItem ->
85-
val newMessage = serverMessageItem.message.processHistoryMessage(pubnub.cryptoModule, pubnub.mapper)
85+
val (newMessage, error) = serverMessageItem.message.processHistoryMessage(
86+
pubnub.cryptoModule,
87+
pubnub.mapper
88+
)
8689
val newActions =
8790
if (includeMessageActions) serverMessageItem.actions ?: mapOf() else serverMessageItem.actions
8891
PNFetchMessageItem(
@@ -92,6 +95,7 @@ class FetchMessages internal constructor(
9295
timetoken = serverMessageItem.timetoken,
9396
actions = newActions,
9497
messageType = if (includeMessageType) HistoryMessageType.of(serverMessageItem.messageType) else null,
98+
error = error
9599
)
96100
}
97101
}.toMap()

src/main/kotlin/com/pubnub/api/endpoints/History.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,30 @@ class History internal constructor(
7171

7272
val historyEntry = iterator.next()
7373

74-
var message: JsonElement
7574
var timetoken: Long? = null
7675
var meta: JsonElement? = null
76+
val historyMessageWithError: Pair<JsonElement, PubNubError?>
7777

7878
if (includeTimetoken || includeMeta) {
79-
message = pubnub.mapper.getField(historyEntry, "message")!!.processHistoryMessage(pubnub.cryptoModule, pubnub.mapper)
79+
historyMessageWithError = pubnub.mapper.getField(historyEntry, "message")!!.processHistoryMessage(pubnub.cryptoModule, pubnub.mapper)
8080
if (includeTimetoken) {
8181
timetoken = pubnub.mapper.elementToLong(historyEntry, "timetoken")
8282
}
8383
if (includeMeta) {
8484
meta = pubnub.mapper.getField(historyEntry, "meta")
8585
}
8686
} else {
87-
message = historyEntry.processHistoryMessage(pubnub.cryptoModule, pubnub.mapper)
87+
historyMessageWithError = historyEntry.processHistoryMessage(pubnub.cryptoModule, pubnub.mapper)
8888
}
8989

90+
val message: JsonElement = historyMessageWithError.first
91+
val error: PubNubError? = historyMessageWithError.second
92+
9093
val historyItem = PNHistoryItemResult(
9194
entry = message,
9295
timetoken = timetoken,
93-
meta = meta
96+
meta = meta,
97+
error = error
9498
)
9599

96100
messages.add(historyItem)

src/main/kotlin/com/pubnub/api/models/consumer/history/PNFetchMessage.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.pubnub.api.models.consumer.history
22

33
import com.google.gson.JsonElement
44
import com.pubnub.api.PubNub
5+
import com.pubnub.api.PubNubError
56
import com.pubnub.api.endpoints.FetchMessages
67
import com.pubnub.api.models.consumer.PNBoundedPage
78

@@ -28,17 +29,20 @@ data class PNFetchMessagesResult(
2829
* The key of the map is the action type. The value is another map,
2930
* which key is the actual value of the message action,
3031
* and the key being a list of actions, ie. a list of UUIDs which have posted such a message action.
31-
* @property messageType The message type associated with the item.
32-
*
3332
* @see [Action]
33+
* @property messageType The message type associated with the item.
34+
* @property error The error associated with message retrieval, if any.
35+
* e.g. a message is unencrypted but PubNub instance is configured with the Crypto
36+
* so PubNub can't decrypt the unencrypted message and return the message.
3437
*/
3538
data class PNFetchMessageItem(
3639
val uuid: String?,
3740
val message: JsonElement,
3841
val meta: JsonElement?,
3942
val timetoken: Long,
4043
val actions: Map<String, Map<String, List<Action>>>? = null,
41-
val messageType: HistoryMessageType?
44+
val messageType: HistoryMessageType?,
45+
val error: PubNubError? = null
4246
)
4347

4448
/**

src/main/kotlin/com/pubnub/api/models/consumer/history/PNHistoryResult.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.pubnub.api.models.consumer.history
22

33
import com.google.gson.JsonElement
44
import com.pubnub.api.PubNub
5+
import com.pubnub.api.PubNubError
56
import com.pubnub.api.endpoints.History
67

78
/**
@@ -24,9 +25,13 @@ class PNHistoryResult internal constructor(
2425
* @property timetoken Publish timetoken of the message, if requested via [History.includeTimetoken]
2526
* @property meta Metadata of the message, if requested via [History.includeMeta].
2627
* Is `null` if not requested, otherwise an empty string if requested but no associated metadata.
28+
* @property error The error associated with message retrieval, if any.
29+
* e.g. a message is unencrypted but PubNub instance is configured with the Crypto
30+
* so PubNub can't decrypt the unencrypted message and return the message.
2731
*/
2832
data class PNHistoryItemResult(
2933
val entry: JsonElement,
3034
val timetoken: Long? = null,
31-
val meta: JsonElement? = null
35+
val meta: JsonElement? = null,
36+
val error: PubNubError? = null
3237
)

0 commit comments

Comments
 (0)