Backend: UI: Implements authentication.
This commit is contained in:
@@ -5,8 +5,10 @@ import dagger.Provides
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import net.buzzert.kordophone.backend.db.CachedChatDatabase
|
import net.buzzert.kordophone.backend.db.CachedChatDatabase
|
||||||
|
import net.buzzert.kordophone.backend.server.Authentication
|
||||||
import net.buzzert.kordophone.backend.server.ChatRepository
|
import net.buzzert.kordophone.backend.server.ChatRepository
|
||||||
import net.buzzert.kordophone.backend.server.RetrofitAPIClient
|
import net.buzzert.kordophone.backend.server.RetrofitAPIClient
|
||||||
|
import net.buzzert.kordophonedroid.ui.shared.ServerConfigRepository
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -15,9 +17,21 @@ import javax.inject.Singleton
|
|||||||
object AppModule {
|
object AppModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideChatRepository(): ChatRepository {
|
fun provideChatRepository(configRepository: ServerConfigRepository): ChatRepository {
|
||||||
val host = "http://192.168.1.123:5738"
|
val serverConfig = configRepository.serverConfig.value
|
||||||
val client = RetrofitAPIClient(URL(host))
|
val authentication = serverConfig.authentication?.let {
|
||||||
|
Authentication(it.username, it.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is really bad error handling...
|
||||||
|
val baseURL: URL = try { URL(serverConfig.serverName) }
|
||||||
|
catch (e: java.net.MalformedURLException) { URL("http://localhost") }
|
||||||
|
|
||||||
|
val client = RetrofitAPIClient(
|
||||||
|
baseURL = baseURL,
|
||||||
|
authentication = authentication ?: Authentication("", "")
|
||||||
|
)
|
||||||
|
|
||||||
val database = CachedChatDatabase.liveDatabase()
|
val database = CachedChatDatabase.liveDatabase()
|
||||||
return ChatRepository(client, database)
|
return ChatRepository(client, database)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,9 @@ fun ConversationListItem(
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
name,
|
name,
|
||||||
style = TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
style = TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Bold),
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
@@ -140,7 +142,8 @@ fun ConversationListItem(
|
|||||||
.toLocalDateTime()
|
.toLocalDateTime()
|
||||||
),
|
),
|
||||||
modifier = Modifier.align(Alignment.CenterVertically),
|
modifier = Modifier.align(Alignment.CenterVertically),
|
||||||
color = MaterialTheme.colors.onBackground.copy(alpha = 0.4f)
|
color = MaterialTheme.colors.onBackground.copy(alpha = 0.4f),
|
||||||
|
maxLines = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.width(horizontalPadding))
|
Spacer(Modifier.width(horizontalPadding))
|
||||||
@@ -172,7 +175,7 @@ fun UnreadIndicator(size: Dp, modifier: Modifier = Modifier) {
|
|||||||
@Composable
|
@Composable
|
||||||
fun ConversationListItemPreview() {
|
fun ConversationListItemPreview() {
|
||||||
Column(modifier = Modifier.background(MaterialTheme.colors.background)) {
|
Column(modifier = Modifier.background(MaterialTheme.colors.background)) {
|
||||||
ConversationListItem(name = "James Magahern", id = "asdf", lastMessagePreview = "This is a test", date = Date(), isUnread = true) {}
|
ConversationListItem(name = "James MagahernMagahernMagahernMagahernMagahernMagahernMagahern", id = "asdf", lastMessagePreview = "This is a test", date = Date(), isUnread = true) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.buzzert.kordophone.backend.model.Conversation
|
import net.buzzert.kordophone.backend.model.Conversation
|
||||||
|
import net.buzzert.kordophone.backend.server.Authentication
|
||||||
import net.buzzert.kordophone.backend.server.ChatRepository
|
import net.buzzert.kordophone.backend.server.ChatRepository
|
||||||
import net.buzzert.kordophone.backend.server.REPO_LOG
|
import net.buzzert.kordophone.backend.server.REPO_LOG
|
||||||
|
import net.buzzert.kordophone.backend.server.RetrofitAPIClient
|
||||||
import net.buzzert.kordophonedroid.ui.shared.ServerConfigRepository
|
import net.buzzert.kordophonedroid.ui.shared.ServerConfigRepository
|
||||||
|
import java.net.URL
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@@ -33,24 +36,32 @@ class ConversationListViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// TODO: Is this the best place to put these?
|
// Watch for config changes
|
||||||
// TODO: Need error handling (exceptions thrown below)
|
|
||||||
|
|
||||||
// Perform initial sync
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
serverConfigRepository.serverConfig.collect { config ->
|
||||||
|
Log.d(CL_VM_LOG, "Got settings change.")
|
||||||
|
|
||||||
|
// Check settings
|
||||||
|
try {
|
||||||
|
val baseURL = URL(config.serverName)
|
||||||
|
val authentication = config.authentication?.let { serverAuth ->
|
||||||
|
Authentication(serverAuth.username, serverAuth.password)
|
||||||
|
} ?: throw Error("No authentication data.")
|
||||||
|
|
||||||
|
// Make new APIClient
|
||||||
|
val apiClient = RetrofitAPIClient(baseURL, authentication)
|
||||||
|
chatRepository.updateAPIClient(apiClient)
|
||||||
|
} catch (e: Error) {
|
||||||
|
Log.e(CL_VM_LOG, "Error re-creating API client for settings change: $e")
|
||||||
|
} catch (e: java.net.MalformedURLException) {
|
||||||
|
Log.e(CL_VM_LOG, "Malformed server URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform db synchronization
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
chatRepository.synchronize()
|
chatRepository.synchronize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for config changes
|
|
||||||
viewModelScope.launch {
|
|
||||||
serverConfigRepository.serverConfig.collect {
|
|
||||||
Log.d(CL_VM_LOG, "Got settings change.")
|
|
||||||
|
|
||||||
// TODO: Respond to this change.
|
|
||||||
// Should probably just forward this directly to ChatRepository.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start watching for updates
|
// Start watching for updates
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ class MessageListViewModel @Inject constructor(
|
|||||||
private val pendingMessages: MutableStateFlow<List<OutgoingMessage>> = MutableStateFlow(listOf())
|
private val pendingMessages: MutableStateFlow<List<OutgoingMessage>> = MutableStateFlow(listOf())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
// TODO: Need to handle settings changes here!!
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
// Remove pending message after message is delivered.
|
// Remove pending message after message is delivered.
|
||||||
// By now, the repository should've committed this to the store.
|
// By now, the repository should've committed this to the store.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.lightColors
|
import androidx.compose.material.lightColors
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
||||||
private val DarkColorPalette = darkColors(
|
private val DarkColorPalette = darkColors(
|
||||||
primary = Purple200,
|
primary = Purple200,
|
||||||
@@ -56,7 +57,13 @@ fun KordophoneTheme(
|
|||||||
@Composable
|
@Composable
|
||||||
fun KordophoneTopAppBar(title: String, backAction: () -> Unit) {
|
fun KordophoneTopAppBar(title: String, backAction: () -> Unit) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(title) },
|
title = {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = backAction) {
|
IconButton(onClick = backAction) {
|
||||||
Icon(Icons.Filled.ArrowBack, null)
|
Icon(Icons.Filled.ArrowBack, null)
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
<network-security-config>
|
<network-security-config>
|
||||||
<domain-config cleartextTrafficPermitted="true">
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
<domain includeSubdomains="true">192.168.1.123</domain>
|
<domain includeSubdomains="true">192.168.1.123</domain>
|
||||||
|
<domain includeSubdomains="true">tesseract.localdomain</domain>
|
||||||
</domain-config>
|
</domain-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
@@ -46,6 +46,7 @@ dependencies {
|
|||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
implementation 'com.google.code.gson:gson:2.9.0'
|
implementation 'com.google.code.gson:gson:2.9.0'
|
||||||
|
implementation 'com.auth0.android:jwtdecode:2.0.2'
|
||||||
|
|
||||||
// Realm
|
// Realm
|
||||||
implementation "io.realm.kotlin:library-base:${realm_version}"
|
implementation "io.realm.kotlin:library-base:${realm_version}"
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
|
|||||||
|
|
||||||
// Flow for watching for message changes for a given conversation
|
// Flow for watching for message changes for a given conversation
|
||||||
fun messagesChanged(conversation: ModelConversation): Flow<List<ModelMessage>> {
|
fun messagesChanged(conversation: ModelConversation): Flow<List<ModelMessage>> {
|
||||||
return realm.query(Conversation::class, "guid == '${conversation.guid}'")
|
return realm.query(Conversation::class, "guid == $0", conversation.guid)
|
||||||
.find()
|
.find()
|
||||||
.first()
|
.first()
|
||||||
.messages
|
.messages
|
||||||
@@ -142,7 +142,7 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getManagedConversationByGuid(guid: GUID): Conversation {
|
private fun getManagedConversationByGuid(guid: GUID): Conversation {
|
||||||
return realm.query(Conversation::class, "guid == '$guid'")
|
return realm.query(Conversation::class, "guid == $0", guid)
|
||||||
.find()
|
.find()
|
||||||
.first()
|
.first()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
package net.buzzert.kordophone.backend.server
|
package net.buzzert.kordophone.backend.server
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.Authenticator
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.Route
|
||||||
import okhttp3.WebSocket
|
import okhttp3.WebSocket
|
||||||
import okhttp3.WebSocketListener
|
import okhttp3.WebSocketListener
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import retrofit2.create
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
interface APIClient {
|
interface APIClient {
|
||||||
@@ -18,12 +25,86 @@ interface APIClient {
|
|||||||
): WebSocket
|
): WebSocket
|
||||||
}
|
}
|
||||||
|
|
||||||
class RetrofitAPIClient(private val baseURL: URL): APIClient {
|
data class Authentication (
|
||||||
|
val username: String,
|
||||||
|
val password: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
class TokenStore(val authentication: Authentication) {
|
||||||
|
var authenticationToken: String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthenticationInterceptor(
|
||||||
|
val tokenStore: TokenStore
|
||||||
|
): Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
// If empty, allow the 401 to occur so we renew our token.
|
||||||
|
val token = tokenStore.authenticationToken ?:
|
||||||
|
return chain.proceed(chain.request())
|
||||||
|
|
||||||
|
val newRequest = chain.request().newBuilder()
|
||||||
|
.header("Authorization", "Bearer $token")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return chain.proceed(newRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TokenAuthenticator(
|
||||||
|
private val tokenStore: TokenStore,
|
||||||
|
private val baseURL: URL
|
||||||
|
) : Authenticator {
|
||||||
private val retrofit: Retrofit = Retrofit.Builder()
|
private val retrofit: Retrofit = Retrofit.Builder()
|
||||||
.baseUrl(baseURL)
|
.baseUrl(baseURL)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private val apiInterface: APIInterface
|
||||||
|
get() = retrofit.create(APIInterface::class.java)
|
||||||
|
|
||||||
|
override fun authenticate(route: Route?, response: Response): Request? {
|
||||||
|
// Fetch new token
|
||||||
|
val request = AuthenticationRequest(
|
||||||
|
username = tokenStore.authentication.username,
|
||||||
|
password = tokenStore.authentication.password
|
||||||
|
)
|
||||||
|
|
||||||
|
val token = runBlocking {
|
||||||
|
apiInterface.authenticate(request).body()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
// Auth failure.
|
||||||
|
// TODO: How to bubble this up?
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update token store
|
||||||
|
tokenStore.authenticationToken = token.serializedToken
|
||||||
|
|
||||||
|
return response.request().newBuilder()
|
||||||
|
.header("Authorization", "Bearer ${token.serializedToken}")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RetrofitAPIClient(
|
||||||
|
private val baseURL: URL,
|
||||||
|
private val authentication: Authentication,
|
||||||
|
): APIClient {
|
||||||
|
private val tokenStore: TokenStore = TokenStore(authentication)
|
||||||
|
|
||||||
|
private val client: OkHttpClient = OkHttpClient.Builder()
|
||||||
|
.addInterceptor(AuthenticationInterceptor(tokenStore))
|
||||||
|
.authenticator(TokenAuthenticator(tokenStore, baseURL))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val retrofit: Retrofit = Retrofit.Builder()
|
||||||
|
.client(client)
|
||||||
|
.baseUrl(baseURL)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.build()
|
||||||
|
|
||||||
override fun getAPIInterface(): APIInterface {
|
override fun getAPIInterface(): APIInterface {
|
||||||
return retrofit.create(APIInterface::class.java)
|
return retrofit.create(APIInterface::class.java)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.buzzert.kordophone.backend.server
|
package net.buzzert.kordophone.backend.server
|
||||||
|
|
||||||
|
import com.auth0.android.jwt.JWT
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import net.buzzert.kordophone.backend.model.Conversation
|
import net.buzzert.kordophone.backend.model.Conversation
|
||||||
import net.buzzert.kordophone.backend.model.GUID
|
import net.buzzert.kordophone.backend.model.GUID
|
||||||
@@ -30,6 +31,23 @@ data class SendMessageResponse(
|
|||||||
val sentMessageGUID: String,
|
val sentMessageGUID: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class AuthenticationRequest(
|
||||||
|
@SerializedName("username")
|
||||||
|
val username: String,
|
||||||
|
|
||||||
|
@SerializedName("password")
|
||||||
|
val password: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AuthenticationResponse(
|
||||||
|
@SerializedName("jwt")
|
||||||
|
val serializedToken: String,
|
||||||
|
) {
|
||||||
|
fun decodeToken(): JWT {
|
||||||
|
return JWT(serializedToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface APIInterface {
|
interface APIInterface {
|
||||||
@GET("/version")
|
@GET("/version")
|
||||||
suspend fun getVersion(): ResponseBody
|
suspend fun getVersion(): ResponseBody
|
||||||
@@ -50,6 +68,9 @@ interface APIInterface {
|
|||||||
|
|
||||||
@POST("/markConversation")
|
@POST("/markConversation")
|
||||||
suspend fun markConversation(@Query("guid") conversationGUID: String): Response<Void>
|
suspend fun markConversation(@Query("guid") conversationGUID: String): Response<Void>
|
||||||
|
|
||||||
|
@POST("/authenticate")
|
||||||
|
suspend fun authenticate(@Body request: AuthenticationRequest): Response<AuthenticationResponse>
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResponseDecodeError(val response: ResponseBody): Exception(response.string())
|
class ResponseDecodeError(val response: ResponseBody): Exception(response.string())
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const val REPO_LOG: String = "ChatRepository"
|
|||||||
const val CONVERSATION_MESSAGE_SYNC_COUNT = 10
|
const val CONVERSATION_MESSAGE_SYNC_COUNT = 10
|
||||||
|
|
||||||
class ChatRepository(
|
class ChatRepository(
|
||||||
private val apiClient: APIClient,
|
apiClient: APIClient,
|
||||||
private val database: CachedChatDatabase,
|
private val database: CachedChatDatabase,
|
||||||
) {
|
) {
|
||||||
sealed class Error {
|
sealed class Error {
|
||||||
@@ -84,14 +84,26 @@ class ChatRepository(
|
|||||||
val requestGuid: GUID,
|
val requestGuid: GUID,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val apiInterface = apiClient.getAPIInterface()
|
private var apiInterface = apiClient.getAPIInterface()
|
||||||
private val outgoingMessageQueue: ArrayBlockingQueue<OutgoingMessageInfo> = ArrayBlockingQueue(16)
|
private val outgoingMessageQueue: ArrayBlockingQueue<OutgoingMessageInfo> = ArrayBlockingQueue(16)
|
||||||
private var outgoingMessageThread: Thread? = null
|
private var outgoingMessageThread: Thread? = null
|
||||||
private val _messageDeliveredChannel = MutableSharedFlow<MessageDeliveredEvent>()
|
private val _messageDeliveredChannel = MutableSharedFlow<MessageDeliveredEvent>()
|
||||||
private val _errorEncounteredChannel = MutableSharedFlow<Error>()
|
private val _errorEncounteredChannel = MutableSharedFlow<Error>()
|
||||||
|
|
||||||
private val updateMonitor = UpdateMonitor(apiClient)
|
private var updateMonitor = UpdateMonitor(apiClient)
|
||||||
private var updateWatchJob: Job? = null
|
private var updateWatchJob: Job? = null
|
||||||
|
private var updateWatchScope: CoroutineScope? = null
|
||||||
|
|
||||||
|
fun updateAPIClient(client: APIClient) {
|
||||||
|
this.apiInterface = client.getAPIInterface()
|
||||||
|
this.updateMonitor = UpdateMonitor(client)
|
||||||
|
|
||||||
|
// Restart update watch job, if necessary.
|
||||||
|
if (this.updateWatchJob != null) {
|
||||||
|
stopWatchingForUpdates()
|
||||||
|
beginWatchingForUpdates(updateWatchScope!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getVersion(): String {
|
suspend fun getVersion(): String {
|
||||||
return apiInterface.getVersion().string()
|
return apiInterface.getVersion().string()
|
||||||
@@ -111,6 +123,7 @@ class ChatRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateWatchScope = scope
|
||||||
updateMonitor.beginMonitoringUpdates()
|
updateMonitor.beginMonitoringUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import net.buzzert.kordophone.backend.model.Message
|
|||||||
import net.buzzert.kordophone.backend.model.UpdateItem
|
import net.buzzert.kordophone.backend.model.UpdateItem
|
||||||
import net.buzzert.kordophone.backend.server.APIClient
|
import net.buzzert.kordophone.backend.server.APIClient
|
||||||
import net.buzzert.kordophone.backend.server.APIInterface
|
import net.buzzert.kordophone.backend.server.APIInterface
|
||||||
|
import net.buzzert.kordophone.backend.server.AuthenticationRequest
|
||||||
|
import net.buzzert.kordophone.backend.server.AuthenticationResponse
|
||||||
import net.buzzert.kordophone.backend.server.SendMessageRequest
|
import net.buzzert.kordophone.backend.server.SendMessageRequest
|
||||||
import net.buzzert.kordophone.backend.server.SendMessageResponse
|
import net.buzzert.kordophone.backend.server.SendMessageResponse
|
||||||
import net.buzzert.kordophone.backend.server.authenticatedWebSocketURL
|
import net.buzzert.kordophone.backend.server.authenticatedWebSocketURL
|
||||||
@@ -257,4 +259,15 @@ class MockServerInterface(private val server: MockServer): APIInterface {
|
|||||||
server.markConversationAsRead(conversationGUID)
|
server.markConversationAsRead(conversationGUID)
|
||||||
return Response.success(null)
|
return Response.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun authenticate(request: AuthenticationRequest): Response<AuthenticationResponse> {
|
||||||
|
// Anything goes!
|
||||||
|
val response = AuthenticationResponse(
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||||
|
"eyJ1c2VybmFtZSI6InRlc3QiLCJleHAiOjE3MDk3OTQ5NjB9." +
|
||||||
|
"82UcI1gB4eARmgrKwAY6JnbEdWLXou1GWp29scnUhi8"
|
||||||
|
)
|
||||||
|
|
||||||
|
return Response.success(response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user