Skip to content

Commit bec0e63

Browse files
authored
Merge pull request #113 from algolia/feature/apikey-remaining-validity
getSecuredApiKeyRemainingValidity
2 parents 6355a05 + 06e0889 commit bec0e63

File tree

5 files changed

+80
-5
lines changed

5 files changed

+80
-5
lines changed

src/commonMain/kotlin/com/algolia/search/client/ClientSearch.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ package com.algolia.search.client
33
import com.algolia.search.configuration.*
44
import com.algolia.search.dsl.requestOptionsBuilder
55
import com.algolia.search.endpoint.*
6+
import com.algolia.search.helper.decodeBase64
67
import com.algolia.search.helper.encodeBase64
78
import com.algolia.search.helper.sha256
89
import com.algolia.search.helper.toAPIKey
9-
import com.algolia.search.model.APIKey
10-
import com.algolia.search.model.ApplicationID
11-
import com.algolia.search.model.IndexName
12-
import com.algolia.search.model.LogType
10+
import com.algolia.search.model.*
1311
import com.algolia.search.model.apikey.SecuredAPIKeyRestriction
1412
import com.algolia.search.model.response.ResponseAPIKey
1513
import com.algolia.search.model.response.ResponseBatches
@@ -173,5 +171,26 @@ public class ClientSearch private constructor(
173171

174172
return "$hash$restrictionString".encodeBase64().toAPIKey()
175173
}
174+
175+
/**
176+
* Gets how many milliseconds are left before the secured API key expires.
177+
*
178+
* @param apiKey The secured API Key to check.
179+
* @return Milliseconds left before the secured API key expires.
180+
* @throws IllegalArgumentException if [apiKey] doesn't have a [SecuredAPIKeyRestriction.validUntil].
181+
*/
182+
public fun getSecuredApiKeyRemainingValidity(apiKey: APIKey): Long {
183+
val decoded = apiKey.raw.decodeBase64()
184+
val pattern = Regex("validUntil=(\\d+)")
185+
val match = pattern.find(decoded)
186+
187+
return if (match != null) {
188+
val timestamp = match.groupValues[1].toLong()
189+
190+
timestamp - Time.getCurrentTimeMillis()
191+
} else {
192+
throw IllegalArgumentException("The Secured API Key doesn't have a validUntil parameter.");
193+
}
194+
}
176195
}
177196
}

src/commonMain/kotlin/com/algolia/search/helper/Hashing.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ package com.algolia.search.helper
33

44
internal expect fun String.sha256(key: String): String
55

6-
internal expect fun String.encodeBase64(): String
6+
internal expect fun String.encodeBase64(): String
7+
8+
internal expect fun String.decodeBase64(): String
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.algolia.search.model.apikey
2+
3+
import com.algolia.search.client.ClientSearch
4+
import com.algolia.search.model.APIKey
5+
6+
7+
public fun APIKey.getSecuredApiKeyRemainingValidity(): Long {
8+
return ClientSearch.getSecuredApiKeyRemainingValidity(this)
9+
}
10+
11+
public fun APIKey.generateSecuredAPIKey(restriction: SecuredAPIKeyRestriction): APIKey {
12+
return ClientSearch.generateAPIKey(this, restriction)
13+
}

src/commonTest/kotlin/suite/TestSecuredAPIKey.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ package suite
33
import clientAdmin1
44
import clientSearch
55
import com.algolia.search.client.ClientSearch
6+
import com.algolia.search.model.APIKey
7+
import com.algolia.search.model.IndexName
68
import com.algolia.search.model.Time
79
import com.algolia.search.model.apikey.SecuredAPIKeyRestriction
10+
import com.algolia.search.model.apikey.generateSecuredAPIKey
11+
import com.algolia.search.model.apikey.getSecuredApiKeyRemainingValidity
812
import com.algolia.search.model.task.TaskStatus
913
import com.algolia.search.serialize.KeyObjectID
1014
import io.ktor.client.features.ResponseException
1115
import kotlinx.serialization.json.json
1216
import runBlocking
17+
import shouldBeTrue
1318
import shouldEqual
1419
import shouldFailWith
1520
import kotlin.test.BeforeTest
@@ -53,4 +58,36 @@ internal class TestSecuredAPIKey {
5358
}
5459
}
5560
}
61+
62+
@Test
63+
fun expiredKey() {
64+
val restrictions = SecuredAPIKeyRestriction(
65+
validUntil = Time.getCurrentTimeMillis() - 600,
66+
restrictIndices = listOf(IndexName("index"))
67+
)
68+
val key = APIKey("parentKey").generateSecuredAPIKey(restrictions)
69+
70+
(key.getSecuredApiKeyRemainingValidity() < 0).shouldBeTrue()
71+
}
72+
73+
@Test
74+
fun validKey() {
75+
val restrictions = SecuredAPIKeyRestriction(
76+
validUntil = Time.getCurrentTimeMillis() + 600,
77+
restrictIndices = listOf(IndexName("index"))
78+
)
79+
val key = APIKey("parentKey").generateSecuredAPIKey(restrictions)
80+
81+
(key.getSecuredApiKeyRemainingValidity() > 0).shouldBeTrue()
82+
}
83+
84+
@Test
85+
fun remainingValidityParameter() {
86+
val restrictions = SecuredAPIKeyRestriction(
87+
restrictIndices = listOf(IndexName("index"))
88+
)
89+
val key = APIKey("parentKey").generateSecuredAPIKey(restrictions)
90+
91+
shouldFailWith<IllegalArgumentException> { key.getSecuredApiKeyRemainingValidity() }
92+
}
5693
}

src/jvmMain/kotlin/com/algolia/search/helper/Hashing.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ internal actual fun String.sha256(key: String): String {
1919

2020
internal actual fun String.encodeBase64(): String {
2121
return String(Base64.getEncoder().encode(toByteArray()))
22+
}
23+
24+
internal actual fun String.decodeBase64(): String {
25+
return String(Base64.getDecoder().decode(this))
2226
}

0 commit comments

Comments
 (0)