Skip to content

Legals #4660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Dec 10, 2021
Merged

Legals #4660

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4638.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a help section in the settings.
1 change: 1 addition & 0 deletions changelog.d/4660.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create a legal screen in the setting to group all the different policies.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.matrix.android.sdk.api.session.terms

import org.matrix.android.sdk.internal.session.terms.TermsResponse

interface TermsService {
enum class ServiceType {
IntegrationManager,
Expand All @@ -28,4 +30,10 @@ interface TermsService {
baseUrl: String,
agreedUrls: List<String>,
token: String?)

/**
* Get the homeserver terms, from the register API.
* Will be updated once https://github.com./matrix-org/matrix-doc/pull/3012 will be implemented.
*/
suspend fun getHomeserverTerms(baseUrl: String): TermsResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ package org.matrix.android.sdk.internal.session.terms

import dagger.Lazy
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.network.RetrofitFactory
Expand Down Expand Up @@ -55,6 +58,27 @@ internal class DefaultTermsService @Inject constructor(
return GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData())
}

/**
* We use a trick here to get the homeserver T&C, we use the register API
*/
override suspend fun getHomeserverTerms(baseUrl: String): TermsResponse {
return try {
executeRequest(null) {
termsAPI.register(baseUrl + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this something we should consider adding to the spec? it looks like we're relying on the register endpoint to fail

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the register endpoint will fail for sure, we are not providing any data to the request. Also if registration is disabled, I'm not sure what will happen.
This is a temporary workaround waiting for matrix-org/matrix-spec-proposals#3012 to land

}
// Return empty result if it succeed, but it should never happen
TermsResponse()
} catch (throwable: Throwable) {
@Suppress("UNCHECKED_CAST")
TermsResponse(
policies = (throwable.toRegistrationFlowResponse()
?.params
?.get(LoginFlowTypes.TERMS) as? JsonDict)
?.get("policies") as? JsonDict
)
}
}

override suspend fun agreeToTerms(serviceType: TermsService.ServiceType,
baseUrl: String,
agreedUrls: List<String>,
Expand Down Expand Up @@ -91,7 +115,7 @@ internal class DefaultTermsService @Inject constructor(
private fun buildUrl(baseUrl: String, serviceType: TermsService.ServiceType): String {
val servicePath = when (serviceType) {
TermsService.ServiceType.IntegrationManager -> NetworkConstants.URI_INTEGRATION_MANAGER_PATH
TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2
TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2
}
return "${baseUrl.ensureTrailingSlash()}$servicePath"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.matrix.android.sdk.internal.session.terms

import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.emptyJsonDict
import org.matrix.android.sdk.internal.network.HttpHeaders
import retrofit2.http.Body
import retrofit2.http.GET
Expand All @@ -37,4 +39,12 @@ internal interface TermsAPI {
suspend fun agreeToTerms(@Url url: String,
@Body params: AcceptTermsBody,
@Header(HttpHeaders.Authorization) token: String)

/**
* API to retrieve the terms for a homeserver. The API /terms does not exist yet, so retrieve the terms from the login flow.
* We do not care about the result (Credentials)
*/
@POST
suspend fun register(@Url url: String,
@Body body: JsonDict = emptyJsonDict)
}
6 changes: 6 additions & 0 deletions vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ import im.vector.app.features.settings.devtools.KeyRequestsFragment
import im.vector.app.features.settings.devtools.OutgoingKeyRequestListFragment
import im.vector.app.features.settings.homeserver.HomeserverSettingsFragment
import im.vector.app.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.app.features.settings.legals.LegalsFragment
import im.vector.app.features.settings.locale.LocalePickerFragment
import im.vector.app.features.settings.notifications.VectorSettingsAdvancedNotificationPreferenceFragment
import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment
Expand Down Expand Up @@ -699,6 +700,11 @@ interface FragmentModule {
@FragmentKey(DiscoverySettingsFragment::class)
fun bindDiscoverySettingsFragment(fragment: DiscoverySettingsFragment): Fragment

@Binds
@IntoMap
@FragmentKey(LegalsFragment::class)
fun bindLegalsFragment(fragment: LegalsFragment): Fragment

@Binds
@IntoMap
@FragmentKey(ReviewTermsFragment::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import im.vector.app.features.settings.devtools.KeyRequestListViewModel
import im.vector.app.features.settings.devtools.KeyRequestViewModel
import im.vector.app.features.settings.homeserver.HomeserverSettingsViewModel
import im.vector.app.features.settings.ignored.IgnoredUsersViewModel
import im.vector.app.features.settings.legals.LegalsViewModel
import im.vector.app.features.settings.locale.LocalePickerViewModel
import im.vector.app.features.settings.push.PushGatewaysViewModel
import im.vector.app.features.settings.threepids.ThreePidsSettingsViewModel
Expand Down Expand Up @@ -504,6 +505,11 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(DiscoverySettingsViewModel::class)
fun discoverySettingsViewModelFactory(factory: DiscoverySettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>

@Binds
@IntoMap
@MavericksViewModelKey(LegalsViewModel::class)
fun legalsViewModelFactory(factory: LegalsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>

@Binds
@IntoMap
@MavericksViewModelKey(RoomDetailViewModel::class)
Expand Down
4 changes: 2 additions & 2 deletions vector/src/main/java/im/vector/app/core/utils/Dialogs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import android.webkit.WebViewClient
import android.widget.TextView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R
import im.vector.app.features.discovery.IdentityServerWithTerms
import im.vector.app.features.discovery.ServerAndPolicies
import me.gujun.android.span.link
import me.gujun.android.span.span

Expand All @@ -45,7 +45,7 @@ fun Context.displayInWebView(url: String) {
.show()
}

fun Context.showIdentityServerConsentDialog(identityServerWithTerms: IdentityServerWithTerms?,
fun Context.showIdentityServerConsentDialog(identityServerWithTerms: ServerAndPolicies?,
consentCallBack: (() -> Unit)) {
// Build the message
val content = span {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
package im.vector.app.features.contactsbook

import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.features.discovery.IdentityServerWithTerms
import im.vector.app.features.discovery.ServerAndPolicies

sealed class ContactsBookViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : ContactsBookViewEvents()
data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : ContactsBookViewEvents()
data class OnPoliciesRetrieved(val identityServerWithTerms: ServerAndPolicies?) : ContactsBookViewEvents()
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide

@EpoxyModelClass(layout = R.layout.item_discovery_policy)
abstract class DiscoveryPolicyItem : EpoxyModelWithHolder<DiscoveryPolicyItem.Holder>() {
Expand All @@ -40,7 +41,7 @@ abstract class DiscoveryPolicyItem : EpoxyModelWithHolder<DiscoveryPolicyItem.Ho
override fun bind(holder: Holder) {
super.bind(holder)
holder.title.text = name
holder.url.text = url
holder.url.setTextOrHide(url)
holder.view.onClick(clickListener)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,6 @@ class DiscoverySettingsController @Inject constructor(
fun onTapUpdateUserConsent(newValue: Boolean)
fun onTapRetryToRetrieveBindings()
fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean)
fun onPolicyTapped(policy: IdentityServerPolicy)
fun onPolicyTapped(policy: ServerPolicy)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,11 @@ class DiscoverySettingsFragment @Inject constructor(
val pidList = state.emailList().orEmpty() + state.phoneNumbersList().orEmpty()
val hasBoundIds = pidList.any { it.isShared() == SharedState.SHARED }

val serverUrl = state.identityServer()?.serverUrl.orEmpty()
val message = if (hasBoundIds) {
getString(R.string.settings_discovery_disconnect_with_bound_pid, state.identityServer(), state.identityServer())
getString(R.string.settings_discovery_disconnect_with_bound_pid, serverUrl, serverUrl)
} else {
getString(R.string.disconnect_identity_server_dialog_content, state.identityServer())
getString(R.string.disconnect_identity_server_dialog_content, serverUrl)
}

MaterialAlertDialogBuilder(requireActivity())
Expand Down Expand Up @@ -203,7 +204,7 @@ class DiscoverySettingsFragment @Inject constructor(
viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = newExpandedState))
}

override fun onPolicyTapped(policy: IdentityServerPolicy) {
override fun onPolicyTapped(policy: ServerPolicy) {
openUrlInChromeCustomTab(requireContext(), null, policy.url)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized

data class DiscoverySettingsState(
val identityServer: Async<IdentityServerWithTerms?> = Uninitialized,
val identityServer: Async<ServerAndPolicies?> = Uninitialized,
val emailList: Async<List<PidInfo>> = Uninitialized,
val phoneNumbersList: Async<List<PidInfo>> = Uninitialized,
// Can be true if terms are updated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
init {
setState {
copy(
identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { IdentityServerWithTerms(it, emptyList()) }),
identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { ServerAndPolicies(it, emptyList()) }),
userConsent = identityService.getUserConsent()
)
}
Expand Down Expand Up @@ -151,7 +151,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
val data = session.identityService().setNewIdentityServer(action.url)
setState {
copy(
identityServer = Success(IdentityServerWithTerms(data, emptyList())),
identityServer = Success(ServerAndPolicies(data, emptyList())),
userConsent = false
)
}
Expand Down Expand Up @@ -401,7 +401,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
}
}

private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms? {
private suspend fun fetchIdentityServerWithTerms(): ServerAndPolicies? {
return session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language))
}
}
41 changes: 27 additions & 14 deletions vector/src/main/java/im/vector/app/features/discovery/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,35 @@ package im.vector.app.features.discovery
import im.vector.app.core.utils.ensureProtocol
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.internal.session.terms.TermsResponse

suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): IdentityServerWithTerms? {
val identityServerUrl = identityService().getCurrentIdentityServerUrl()
return identityServerUrl?.let {
val terms = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol())
.serverResponse
.getLocalizedTerms(userLanguage)
val policyUrls = terms.mapNotNull {
val name = it.localizedName ?: it.policyName
val url = it.localizedUrl
if (name == null || url == null) {
null
} else {
IdentityServerPolicy(name = name, url = url)
suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): ServerAndPolicies? {
return identityService().getCurrentIdentityServerUrl()
?.let { identityServerUrl ->
val termsResponse = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol())
.serverResponse
buildServerAndPolicies(identityServerUrl, termsResponse, userLanguage)
}
}

suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): ServerAndPolicies {
val homeserverUrl = sessionParams.homeServerUrl
val terms = getHomeserverTerms(homeserverUrl.ensureProtocol())
return buildServerAndPolicies(homeserverUrl, terms, userLanguage)
}

private fun buildServerAndPolicies(serviceUrl: String,
termsResponse: TermsResponse,
userLanguage: String): ServerAndPolicies {
val terms = termsResponse.getLocalizedTerms(userLanguage)
val policyUrls = terms.mapNotNull {
val name = it.localizedName ?: it.policyName
val url = it.localizedUrl
if (name == null || url == null) {
null
} else {
ServerPolicy(name = name, url = url)
}
IdentityServerWithTerms(identityServerUrl, policyUrls)
}
return ServerAndPolicies(serviceUrl, policyUrls)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@

package im.vector.app.features.discovery

data class IdentityServerWithTerms(
data class ServerAndPolicies(
val serverUrl: String,
val policies: List<IdentityServerPolicy>
val policies: List<ServerPolicy>
)

data class IdentityServerPolicy(
data class ServerPolicy(
val name: String,
val url: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,16 @@ import javax.inject.Inject
class VectorPreferences @Inject constructor(private val context: Context) {

companion object {
const val SETTINGS_HELP_PREFERENCE_KEY = "SETTINGS_HELP_PREFERENCE_KEY"
const val SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY = "SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY"
const val SETTINGS_VERSION_PREFERENCE_KEY = "SETTINGS_VERSION_PREFERENCE_KEY"
const val SETTINGS_SDK_VERSION_PREFERENCE_KEY = "SETTINGS_SDK_VERSION_PREFERENCE_KEY"
const val SETTINGS_OLM_VERSION_PREFERENCE_KEY = "SETTINGS_OLM_VERSION_PREFERENCE_KEY"
const val SETTINGS_LOGGED_IN_PREFERENCE_KEY = "SETTINGS_LOGGED_IN_PREFERENCE_KEY"
const val SETTINGS_HOME_SERVER_PREFERENCE_KEY = "SETTINGS_HOME_SERVER_PREFERENCE_KEY"
const val SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY = "SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY"
const val SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY = "SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY"
const val SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY = "SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY"
const val SETTINGS_DISCOVERY_PREFERENCE_KEY = "SETTINGS_DISCOVERY_PREFERENCE_KEY"

const val SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
const val SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
const val SETTINGS_COPYRIGHT_PREFERENCE_KEY = "SETTINGS_COPYRIGHT_PREFERENCE_KEY"
const val SETTINGS_CLEAR_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_CACHE_PREFERENCE_KEY"
const val SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY"
const val SETTINGS_USER_SETTINGS_PREFERENCE_KEY = "SETTINGS_USER_SETTINGS_PREFERENCE_KEY"
Expand Down
Loading