mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 12:00:03 +03:00
Widgets: handle scalar token
This commit is contained in:
parent
ce884ac577
commit
01d6b52a60
10 changed files with 263 additions and 5 deletions
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class ScalarTokenEntity(
|
||||
@PrimaryKey var serverUrl: String = "",
|
||||
var token: String = ""
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
|
||||
}
|
|
@ -54,6 +54,7 @@ import io.realm.annotations.RealmModule
|
|||
HomeServerCapabilitiesEntity::class,
|
||||
RoomMemberSummaryEntity::class,
|
||||
CurrentStateEventEntity::class,
|
||||
UserAccountDataEntity::class
|
||||
UserAccountDataEntity::class,
|
||||
ScalarTokenEntity::class
|
||||
])
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.internal.database.model.ScalarTokenEntity
|
||||
import im.vector.matrix.android.internal.database.model.ScalarTokenEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
|
||||
internal fun ScalarTokenEntity.Companion.where(realm: Realm, serverUrl: String): RealmQuery<ScalarTokenEntity> {
|
||||
return realm
|
||||
.where<ScalarTokenEntity>()
|
||||
.equalTo(ScalarTokenEntityFields.SERVER_URL, serverUrl)
|
||||
}
|
|
@ -55,6 +55,7 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecr
|
|||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||
import im.vector.matrix.android.internal.session.widgets.WidgetManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -99,6 +100,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val accountService: Lazy<AccountService>,
|
||||
private val timelineEventDecryptor: TimelineEventDecryptor,
|
||||
private val integrationManager: IntegrationManager,
|
||||
private val widgetManager: WidgetManager,
|
||||
private val shieldTrustUpdater: ShieldTrustUpdater)
|
||||
: Session,
|
||||
RoomService by roomService.get(),
|
||||
|
@ -136,6 +138,7 @@ internal class DefaultSession @Inject constructor(
|
|||
timelineEventDecryptor.start()
|
||||
shieldTrustUpdater.start()
|
||||
integrationManager.start()
|
||||
widgetManager.start()
|
||||
}
|
||||
|
||||
override fun requireBackgroundSync() {
|
||||
|
@ -179,6 +182,7 @@ internal class DefaultSession @Inject constructor(
|
|||
eventBus.unregister(this)
|
||||
shieldTrustUpdater.stop()
|
||||
integrationManager.stop()
|
||||
widgetManager.stop()
|
||||
}
|
||||
|
||||
override fun getSyncStateLive(): LiveData<SyncState> {
|
||||
|
|
|
@ -53,6 +53,7 @@ import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
|||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||
import im.vector.matrix.android.internal.session.user.UserModule
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule
|
||||
import im.vector.matrix.android.internal.session.widgets.WidgetModule
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
|
||||
|
@ -72,6 +73,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
|||
CryptoModule::class,
|
||||
PushersModule::class,
|
||||
OpenIdModule::class,
|
||||
WidgetModule::class,
|
||||
AccountDataModule::class,
|
||||
ProfileModule::class,
|
||||
SessionAssistedInjectModule::class,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.widgets
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.internal.session.widgets.token.DefaultGetScalarTokenTask
|
||||
import im.vector.matrix.android.internal.session.widgets.token.GetScalarTokenTask
|
||||
import retrofit2.Retrofit
|
||||
|
||||
@Module
|
||||
internal abstract class WidgetModule {
|
||||
|
||||
@Module
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Provides
|
||||
fun providesWidgetsAPI(retrofit: Retrofit): WidgetsAPI {
|
||||
return retrofit.create(WidgetsAPI::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract fun bindCreateWidgetTask(task: DefaultCreateWidgetTask): CreateWidgetTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetScalarTokenTask(task: DefaultGetScalarTokenTask): GetScalarTokenTask
|
||||
}
|
|
@ -15,21 +15,22 @@
|
|||
*/
|
||||
package im.vector.matrix.android.internal.session.widgets
|
||||
|
||||
import im.vector.matrix.android.internal.session.openid.RequestOpenIdTokenResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
internal interface WidgetsAPI {
|
||||
|
||||
/**
|
||||
* register to the server
|
||||
*
|
||||
* @param requestOpenIdTokenResponse the body content (Ref: https://github.com/matrix-org/matrix-doc/pull/1961)
|
||||
*/
|
||||
|
||||
//TODO Require work on RequestOpenIdTokenResponse from Benoit
|
||||
@POST("register")
|
||||
//fun register(@Body requestOpenIdTokenResponse: RequestOpenIdTokenResponse, @Query("v") version: String?): Call<RegisterWidgetResponse>
|
||||
fun register(@Body requestOpenIdTokenResponse: RequestOpenIdTokenResponse, @Query("v") version: String?): Call<RegisterWidgetResponse>
|
||||
|
||||
@GET("account")
|
||||
fun validateToken(@Query("scalar_token") scalarToken: String?, @Query("v") version: String?): Call<Unit>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.widgets
|
||||
|
||||
import dagger.Lazy
|
||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class WidgetsAPIProvider @Inject constructor(@Unauthenticated private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory) {
|
||||
|
||||
// Map to keep one WidgetAPI instance by serverUrl
|
||||
private val widgetsAPIs = mutableMapOf<String, WidgetsAPI>()
|
||||
|
||||
fun get(serverUrl: String): WidgetsAPI {
|
||||
return widgetsAPIs.getOrPut(serverUrl) {
|
||||
retrofitFactory.create(okHttpClient, serverUrl).create(WidgetsAPI::class.java)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.widgets.token
|
||||
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.openid.GetOpenIdTokenTask
|
||||
import im.vector.matrix.android.internal.session.widgets.RegisterWidgetResponse
|
||||
import im.vector.matrix.android.internal.session.widgets.WidgetsAPI
|
||||
import im.vector.matrix.android.internal.session.widgets.WidgetsAPIProvider
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import java.net.HttpURLConnection
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetScalarTokenTask : Task<GetScalarTokenTask.Params, String> {
|
||||
|
||||
data class Params(
|
||||
val serverUrl: String
|
||||
)
|
||||
}
|
||||
|
||||
private const val WIDGET_API_VERSION = "1.1"
|
||||
|
||||
internal class DefaultGetScalarTokenTask @Inject constructor(private val widgetsAPIProvider: WidgetsAPIProvider,
|
||||
private val scalarTokenStore: ScalarTokenStore,
|
||||
private val getOpenIdTokenTask: GetOpenIdTokenTask) : GetScalarTokenTask {
|
||||
|
||||
override suspend fun execute(params: GetScalarTokenTask.Params): String {
|
||||
val widgetsAPI = widgetsAPIProvider.get(params.serverUrl)
|
||||
val scalarToken = scalarTokenStore.getToken(params.serverUrl)
|
||||
return if (scalarToken == null) {
|
||||
getNewScalarToken(widgetsAPI, params.serverUrl)
|
||||
} else {
|
||||
validateToken(widgetsAPI, params.serverUrl, scalarToken)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNewScalarToken(widgetsAPI: WidgetsAPI, serverUrl: String): String {
|
||||
val openId = getOpenIdTokenTask.execute(Unit)
|
||||
val registerWidgetResponse = executeRequest<RegisterWidgetResponse>(null) {
|
||||
apiCall = widgetsAPI.register(openId, WIDGET_API_VERSION)
|
||||
}
|
||||
if (registerWidgetResponse.scalarToken == null) {
|
||||
// Should not happen
|
||||
throw IllegalStateException("Scalar token is null")
|
||||
}
|
||||
scalarTokenStore.setToken(serverUrl, registerWidgetResponse.scalarToken)
|
||||
widgetsAPI.validateToken(registerWidgetResponse.scalarToken, WIDGET_API_VERSION)
|
||||
return registerWidgetResponse.scalarToken
|
||||
}
|
||||
|
||||
private suspend fun validateToken(widgetsAPI: WidgetsAPI, serverUrl: String, scalarToken: String): String {
|
||||
return try {
|
||||
widgetsAPI.validateToken(scalarToken, WIDGET_API_VERSION)
|
||||
scalarToken
|
||||
} catch (failure: Throwable) {
|
||||
if (failure.isScalarTokenError()) {
|
||||
scalarTokenStore.clearToken(serverUrl)
|
||||
getNewScalarToken(widgetsAPI, serverUrl)
|
||||
} else {
|
||||
throw failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Throwable.isScalarTokenError(): Boolean {
|
||||
return this is Failure.ServerError && this.httpCode == HttpURLConnection.HTTP_FORBIDDEN
|
||||
}
|
||||
}
|
|
@ -16,5 +16,33 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.widgets.token
|
||||
|
||||
internal class ScalarTokenStore {
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.ScalarTokenEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import im.vector.matrix.android.internal.util.fetchCopyMap
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class ScalarTokenStore @Inject constructor(private val monarchy: Monarchy) {
|
||||
|
||||
fun getToken(apiUrl: String): String? {
|
||||
return monarchy.fetchCopyMap({ realm ->
|
||||
ScalarTokenEntity.where(realm, apiUrl).findFirst()
|
||||
}, { scalarToken, _ ->
|
||||
scalarToken.serverUrl
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun setToken(apiUrl: String, token: String) {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val scalarTokenEntity = ScalarTokenEntity(apiUrl, token)
|
||||
realm.insertOrUpdate(scalarTokenEntity)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearToken(apiUrl: String) {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
ScalarTokenEntity.where(realm, apiUrl).findFirst()?.deleteFromRealm()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue