Skip to content
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

Feature/bca/room caps restricted #3663

Merged
merged 11 commits into from
Jul 30, 2021
Merged
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/3509.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Spaces - Support Restricted Room via room capabilities API
1 change: 1 addition & 0 deletions changelog.d/3665.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Spaces | Support restricted room access in room settings
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,63 @@ data class HomeServerCapabilities(
*/
val roomVersions: RoomVersionCapabilities? = null
) {

enum class RoomCapabilitySupport {
SUPPORTED,
SUPPORTED_UNSTABLE,
UNSUPPORTED,
UNKNOWN
}

/**
* Check if a feature is supported by the homeserver.
* @return
* UNKNOWN if the server does not implement room caps
* UNSUPPORTED if this feature is not supported
* SUPPORTED if this feature is supported by a stable version
* SUPPORTED_UNSTABLE if this feature is supported by an unstable version
* (unstable version should only be used for dev/experimental purpose)
*/
fun isFeatureSupported(feature: String): RoomCapabilitySupport {
if (roomVersions?.capabilities == null) return RoomCapabilitySupport.UNKNOWN
val info = roomVersions.capabilities[feature] ?: return RoomCapabilitySupport.UNSUPPORTED

val preferred = info.preferred ?: info.support.lastOrNull()
val versionCap = roomVersions.supportedVersion.firstOrNull { it.version == preferred }

return when {
versionCap == null -> {
RoomCapabilitySupport.UNKNOWN
}
versionCap.status == RoomVersionStatus.STABLE -> {
RoomCapabilitySupport.SUPPORTED
}
else -> {
RoomCapabilitySupport.SUPPORTED_UNSTABLE
}
}
}
fun isFeatureSupported(feature: String, byRoomVersion: String): Boolean {
if (roomVersions?.capabilities == null) return false
val info = roomVersions.capabilities[feature] ?: return false

return info.preferred == byRoomVersion || info.support.contains(byRoomVersion)
}

/**
* Use this method to know if you should force a version when creating
* a room that requires this feature.
* You can also use #isFeatureSupported prior to this call to check if the
* feature is supported and report some feedback to user.
*/
fun versionOverrideForFeature(feature: String) : String? {
Copy link
Member

Choose a reason for hiding this comment

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

Can you add some doc for this public APIs?

Copy link
Member Author

Choose a reason for hiding this comment

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

done

val cap = roomVersions?.capabilities?.get(feature)
return cap?.preferred ?: cap?.support?.lastOrNull()
}

companion object {
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
const val ROOM_CAP_KNOCK = "knock"
const val ROOM_CAP_RESTRICTED = "restricted"
Copy link
Member

Choose a reason for hiding this comment

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

Prefix with ROOM_FEATURE ? The other naming is like that (isFeatureSupported(), ...)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ package org.matrix.android.sdk.api.session.homeserver

data class RoomVersionCapabilities(
val defaultRoomVersion: String,
val supportedVersion: List<RoomVersionInfo>
val supportedVersion: List<RoomVersionInfo>,
// Keys are capabilities defined per spec, as for now knock or restricted
val capabilities: Map<String, RoomCapabilitySupport>?
Copy link
Member

Choose a reason for hiding this comment

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

What are the keys?

Copy link
Member Author

Choose a reason for hiding this comment

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

added a comment

)

data class RoomVersionInfo(
val version: String,
val status: RoomVersionStatus
)

data class RoomCapabilitySupport(
val preferred: String?,
val support: List<String>
)

enum class RoomVersionStatus {
STABLE,
UNSTABLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM

open class CreateRoomParams {
Expand Down Expand Up @@ -162,7 +161,7 @@ open class CreateRoomParams {

var roomVersion: String? = null

var joinRuleRestricted: List<RoomJoinRulesAllowEntry>? = null
var featurePreset: RoomFeaturePreset? = null
Copy link
Member

Choose a reason for hiding this comment

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

See my other comment about SDK2 abstraction.


companion object {
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.session.room.model.create

import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent

interface RoomFeaturePreset {
Copy link
Member

Choose a reason for hiding this comment

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

Interface and class must be moved to internal package and declared internal.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's public, used in CreateSpaceViewModelTask for example


fun updateRoomParams(params: CreateRoomParams)

fun setupInitialStates(): List<Event>?
}

class RestrictedRoomPreset(val homeServerCapabilities: HomeServerCapabilities, val restrictedList: List<RoomJoinRulesAllowEntry>) : RoomFeaturePreset {
Copy link
Member

Choose a reason for hiding this comment

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

This is not ideal from a SDK2 user point of view to provide homeServerCapabilities here. Can you improve the SDK2 API please?


override fun updateRoomParams(params: CreateRoomParams) {
params.historyVisibility = params.historyVisibility ?: RoomHistoryVisibility.SHARED
params.guestAccess = params.guestAccess ?: GuestAccess.Forbidden
params.roomVersion = homeServerCapabilities.versionOverrideForFeature(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
}

override fun setupInitialStates(): List<Event>? {
return listOf(
Event(
type = EventType.STATE_ROOM_JOIN_RULES,
stateKey = "",
content = RoomJoinRulesContent(
_joinRules = RoomJoinRules.RESTRICTED.value,
allowList = restrictedList
).toContent()
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.Optional

Expand Down Expand Up @@ -53,7 +54,7 @@ interface StateService {
/**
* Update the join rule and/or the guest access
*/
suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?)
suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, allowList: List<RoomJoinRulesAllowEntry>? = null)

/**
* Update the avatar of the room
Expand Down Expand Up @@ -91,4 +92,8 @@ interface StateService {
* @param eventTypes Set of eventType to observe. If empty, all state events will be observed
*/
fun getStateEventsLive(eventTypes: Set<String>, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData<List<Event>>

suspend fun setJoinRulePublic()
suspend fun setJoinRuleInviteOnly()
suspend fun setJoinRuleRestricted(allowList: List<String>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.mapper

import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.homeserver.RoomCapabilitySupport
import org.matrix.android.sdk.api.session.homeserver.RoomVersionCapabilities
import org.matrix.android.sdk.api.session.homeserver.RoomVersionInfo
import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus
Expand Down Expand Up @@ -45,19 +46,28 @@ internal object HomeServerCapabilitiesMapper {
roomVersionsJson ?: return null

return tryOrNull {
MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).fromJson(roomVersionsJson)?.let {
MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).fromJson(roomVersionsJson)?.let { roomVersions ->
RoomVersionCapabilities(
defaultRoomVersion = it.default ?: DefaultRoomVersionService.DEFAULT_ROOM_VERSION,
supportedVersion = it.available.entries.map { entry ->
RoomVersionInfo(
version = entry.key,
status = if (entry.value == "stable") {
RoomVersionStatus.STABLE
} else {
RoomVersionStatus.UNSTABLE
}
)
}
defaultRoomVersion = roomVersions.default ?: DefaultRoomVersionService.DEFAULT_ROOM_VERSION,
supportedVersion = roomVersions.available?.entries?.map { entry ->
RoomVersionInfo(entry.key, RoomVersionStatus.STABLE
.takeIf { entry.value == "stable" }
?: RoomVersionStatus.UNSTABLE)
}.orEmpty(),
capabilities = roomVersions.roomCapabilities?.entries?.mapNotNull { entry ->
(entry.value as? Map<*, *>)?.let {
val preferred = it["preferred"] as? String ?: return@mapNotNull null
val support = (it["support"] as? List<*>)?.filterIsInstance<String>()
entry.key to RoomCapabilitySupport(preferred, support.orEmpty())
}
}?.toMap()
// Just for debug purpose
// ?: mapOf(
// HomeServerCapabilities.ROOM_CAP_RESTRICTED to RoomCapabilitySupport(
// preferred = null,
// support = listOf("org.matrix.msc3083")
// )
// )
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,22 @@ internal data class RoomVersions(
* Required. A detailed description of the room versions the server supports.
*/
@Json(name = "available")
val available: JsonDict
val available: JsonDict? = null,

/**
* "room_capabilities": {
* "knock" : {
* "preferred": "7",
* "support" : ["7"]
* },
* "restricted" : {
* "preferred": "9",
* "support" : ["8", "9"]
* }
* }
*/
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the Json sample :)

@Json(name = "room_capabilities")
val roomCapabilities: JsonDict? = null
)

// The spec says: If not present, the client should assume that password changes are possible via the API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,24 @@
package org.matrix.android.sdk.internal.session.permalinks

import org.matrix.android.sdk.api.MatrixPatterns.getDomain
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import java.net.URLEncoder
import javax.inject.Inject
import javax.inject.Provider

internal class ViaParameterFinder @Inject constructor(
@UserId private val userId: String,
private val roomGetterProvider: Provider<RoomGetter>
private val roomGetterProvider: Provider<RoomGetter>,
private val stateEventDataSource: StateEventDataSource
) {

fun computeViaParams(roomId: String, max: Int): List<String> {
Expand Down Expand Up @@ -70,4 +77,28 @@ internal class ViaParameterFinder @Inject constructor(
.orEmpty()
.toSet()
}

fun computeViaParamsForRestricted(roomId: String, max: Int): List<String> {
val userThatCanInvite = roomGetterProvider.get().getRoom(roomId)
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
?.map { it.userId }
?.filter { userCanInvite(userId, roomId) }
.orEmpty()
.toSet()

return userThatCanInvite.map { it.getDomain() }
.groupBy { it }
.mapValues { it.value.size }
.toMutableMap()
.let { map -> map.keys.sortedByDescending { map[it] } }
.take(max)
}

fun userCanInvite(userId: String, roomId: String): Boolean {
Copy link
Member

Choose a reason for hiding this comment

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

private ?

val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
?.content?.toModel<PowerLevelsContent>()
?.let { PowerLevelsHelper(it) }
Copy link
Member

Choose a reason for hiding this comment

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

This code is a bit too much duplicated, we could create a PowerLevelsHelperCreator in the SDK or an extension...


return powerLevelsHelper?.isUserAbleToInvite(userId) ?: false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,10 @@
package org.matrix.android.sdk.internal.session.room.create

import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
import org.matrix.android.sdk.api.session.identity.toMedium
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.internal.crypto.DeviceListManager
Expand All @@ -45,7 +39,6 @@ import javax.inject.Inject

internal class CreateRoomBodyBuilder @Inject constructor(
private val ensureIdentityTokenTask: EnsureIdentityTokenTask,
private val crossSigningService: CrossSigningService,
private val deviceListManager: DeviceListManager,
private val identityStore: IdentityStore,
private val fileUploader: FileUploader,
Expand Down Expand Up @@ -76,19 +69,18 @@ internal class CreateRoomBodyBuilder @Inject constructor(
}
}

if (params.joinRuleRestricted != null) {
params.roomVersion = "org.matrix.msc3083"
params.historyVisibility = params.historyVisibility ?: RoomHistoryVisibility.SHARED
params.guestAccess = params.guestAccess ?: GuestAccess.Forbidden
}
val initialStates = (listOfNotNull(
buildEncryptionWithAlgorithmEvent(params),
buildHistoryVisibilityEvent(params),
buildAvatarEvent(params),
buildGuestAccess(params),
buildJoinRulesRestricted(params)
)
+ buildCustomInitialStates(params))
params.featurePreset?.updateRoomParams(params)
Copy link
Member

Choose a reason for hiding this comment

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

weird code


val initialStates = (
listOfNotNull(
buildEncryptionWithAlgorithmEvent(params),
buildHistoryVisibilityEvent(params),
buildAvatarEvent(params),
buildGuestAccess(params)
)
+ params.featurePreset?.setupInitialStates().orEmpty()
+ buildCustomInitialStates(params)
)
.takeIf { it.isNotEmpty() }

return CreateRoomBody(
Expand Down Expand Up @@ -158,20 +150,6 @@ internal class CreateRoomBodyBuilder @Inject constructor(
}
}

private fun buildJoinRulesRestricted(params: CreateRoomParams): Event? {
return params.joinRuleRestricted
?.let { allowList ->
Event(
type = EventType.STATE_ROOM_JOIN_RULES,
stateKey = "",
content = RoomJoinRulesContent(
_joinRules = RoomJoinRules.RESTRICTED.value,
allowList = allowList
).toContent()
)
}
}

/**
* Add the crypto algorithm to the room creation parameters.
*/
Expand Down
Loading