Merge pull request #4060 from vector-im/feature/bca/space_validate_alias
Client side validation of alias max length
This commit is contained in:
commit
4af9e2c9f0
1
changelog.d/3934.bugfix
Normal file
1
changelog.d/3934.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Validate public space addresses and room aliases length
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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
|
||||
|
||||
/**
|
||||
* This object define some global constants regarding the Matrix specification
|
||||
*/
|
||||
object MatrixConstants {
|
||||
/**
|
||||
* Max length for an alias. Room aliases MUST NOT exceed 255 bytes (including the # sigil and the domain).
|
||||
* See [maxAliasLocalPartLength]
|
||||
* Ref. https://matrix.org/docs/spec/appendices#room-aliases
|
||||
*/
|
||||
const val ALIAS_MAX_LENGTH = 255
|
||||
|
||||
fun maxAliasLocalPartLength(domain: String): Int {
|
||||
return (ALIAS_MAX_LENGTH - 1 /* # sigil */ - 1 /* ':' */ - domain.length)
|
||||
.coerceAtLeast(0)
|
||||
}
|
||||
}
|
@ -17,6 +17,8 @@
|
||||
package org.matrix.android.sdk.api
|
||||
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.internal.util.removeInvalidRoomNameChars
|
||||
import org.matrix.android.sdk.internal.util.replaceSpaceChars
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
@ -130,8 +132,8 @@ object MatrixPatterns {
|
||||
fun isEventId(str: String?): Boolean {
|
||||
return str != null &&
|
||||
(str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER ||
|
||||
str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 ||
|
||||
str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
||||
str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 ||
|
||||
str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,10 +164,11 @@ object MatrixPatterns {
|
||||
return order != null && order.length < 50 && order matches ORDER_STRING_REGEX
|
||||
}
|
||||
|
||||
fun candidateAliasFromRoomName(name: String): String {
|
||||
return Regex("\\s").replace(name.lowercase(), "_").let {
|
||||
"[^a-z0-9._%#@=+-]".toRegex().replace(it, "")
|
||||
}
|
||||
fun candidateAliasFromRoomName(roomName: String, domain: String): String {
|
||||
return roomName.lowercase()
|
||||
.replaceSpaceChars(replacement = "_")
|
||||
.removeInvalidRoomNameChars()
|
||||
.take(MatrixConstants.maxAliasLocalPartLength(domain))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,7 +78,7 @@ internal val spaceChars = "[\u00A0\u2000-\u200B\u2800\u3000]".toRegex()
|
||||
/**
|
||||
* Strip all the UTF-8 chars which are actually spaces
|
||||
*/
|
||||
internal fun String.replaceSpaceChars() = replace(spaceChars, "")
|
||||
internal fun String.replaceSpaceChars(replacement: String = "") = replace(spaceChars, replacement)
|
||||
|
||||
// String.capitalize is now deprecated
|
||||
internal fun String.safeCapitalize(): String {
|
||||
@ -90,3 +90,5 @@ internal fun String.safeCapitalize(): String {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun String.removeInvalidRoomNameChars() = "[^a-z0-9._%#@=+-]".toRegex().replace(this, "")
|
||||
|
@ -17,6 +17,7 @@
|
||||
package im.vector.app.features.form
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.InputFilter
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.TextView
|
||||
@ -77,6 +78,9 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
|
||||
@EpoxyAttribute
|
||||
var suffixText: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var maxLength: Int? = null
|
||||
|
||||
private val onTextChangeListener = object : SimpleTextWatcher() {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
onTextChange?.invoke(s.toString())
|
||||
@ -109,6 +113,15 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
|
||||
holder.textInputEditText.addTextChangedListenerOnce(onTextChangeListener)
|
||||
holder.textInputEditText.setOnEditorActionListener(editorActionListener)
|
||||
holder.textInputEditText.onFocusChangeListener = onFocusChangedListener
|
||||
|
||||
if (maxLength != null) {
|
||||
holder.textInputEditText.filters = arrayOf(InputFilter.LengthFilter(maxLength!!))
|
||||
holder.textInputLayout.isCounterEnabled = true
|
||||
holder.textInputLayout.counterMaxLength = maxLength!!
|
||||
} else {
|
||||
holder.textInputEditText.filters = arrayOf()
|
||||
holder.textInputLayout.isCounterEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldSaveViewState(): Boolean {
|
||||
|
@ -29,6 +29,7 @@ import im.vector.app.features.form.formEditTextItem
|
||||
import im.vector.app.features.form.formEditableAvatarItem
|
||||
import im.vector.app.features.form.formSubmitButtonItem
|
||||
import im.vector.app.features.form.formSwitchItem
|
||||
import org.matrix.android.sdk.api.MatrixConstants
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
@ -141,6 +142,7 @@ class CreateRoomController @Inject constructor(
|
||||
value(viewState.aliasLocalPart)
|
||||
suffixText(":" + viewState.homeServerName)
|
||||
prefixText("#")
|
||||
maxLength(MatrixConstants.maxAliasLocalPartLength(viewState.homeServerName))
|
||||
hint(host.stringProvider.getString(R.string.room_alias_address_hint))
|
||||
errorMessage(
|
||||
host.roomAliasErrorFormatter.format(
|
||||
|
@ -56,7 +56,7 @@ import timber.log.Timber
|
||||
class CreateRoomViewModel @AssistedInject constructor(@Assisted private val initialState: CreateRoomViewState,
|
||||
private val session: Session,
|
||||
private val rawService: RawService,
|
||||
private val vectorPreferences: VectorPreferences
|
||||
vectorPreferences: VectorPreferences
|
||||
) : VectorViewModel<CreateRoomViewState, CreateRoomAction, CreateRoomViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -28,6 +28,7 @@ import im.vector.app.features.form.formEditTextItem
|
||||
import im.vector.app.features.form.formEditableSquareAvatarItem
|
||||
import im.vector.app.features.form.formMultiLineEditTextItem
|
||||
import im.vector.app.features.form.formSubmitButtonItem
|
||||
import org.matrix.android.sdk.api.MatrixConstants
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import javax.inject.Inject
|
||||
@ -81,6 +82,7 @@ class CreateSubSpaceController @Inject constructor(
|
||||
hint(host.stringProvider.getString(R.string.create_space_alias_hint))
|
||||
suffixText(":" + data.homeServerName)
|
||||
prefixText("#")
|
||||
maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName))
|
||||
errorMessage(
|
||||
host.roomAliasErrorFormatter.format(
|
||||
(((data.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError)
|
||||
|
@ -36,6 +36,7 @@ import im.vector.app.features.discovery.settingsInfoItem
|
||||
import im.vector.app.features.form.formEditTextItem
|
||||
import im.vector.app.features.form.formSwitchItem
|
||||
import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter
|
||||
import org.matrix.android.sdk.api.MatrixConstants
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
@ -174,6 +175,7 @@ class RoomAliasController @Inject constructor(
|
||||
formEditTextItem {
|
||||
id("publishManuallyEdit")
|
||||
value(data.publishManuallyState.value)
|
||||
maxLength(MatrixConstants.ALIAS_MAX_LENGTH)
|
||||
hint(host.stringProvider.getString(R.string.room_alias_address_hint))
|
||||
inputType(InputType.TYPE_CLASS_TEXT)
|
||||
onTextChange { text ->
|
||||
@ -253,6 +255,7 @@ class RoomAliasController @Inject constructor(
|
||||
value(data.newLocalAliasState.value)
|
||||
suffixText(":" + data.homeServerName)
|
||||
prefixText("#")
|
||||
maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName))
|
||||
hint(host.stringProvider.getString(R.string.room_alias_address_hint))
|
||||
errorMessage(host.roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError))
|
||||
onTextChange { value ->
|
||||
|
@ -28,7 +28,7 @@ data class CreateSpaceState(
|
||||
val step: Step = Step.ChooseType,
|
||||
val spaceType: SpaceType? = null,
|
||||
val spaceTopology: SpaceTopology? = null,
|
||||
val homeServerName: String? = null,
|
||||
val homeServerName: String = "",
|
||||
val aliasLocalPart: String? = null,
|
||||
val aliasManuallyModified: Boolean = false,
|
||||
val aliasVerificationTask: Async<Boolean> = Uninitialized,
|
||||
|
@ -134,7 +134,7 @@ class CreateSpaceViewModel @AssistedInject constructor(
|
||||
)
|
||||
} else {
|
||||
val tentativeAlias =
|
||||
MatrixPatterns.candidateAliasFromRoomName(action.name)
|
||||
MatrixPatterns.candidateAliasFromRoomName(action.name, homeServerName)
|
||||
copy(
|
||||
nameInlineError = null,
|
||||
name = action.name,
|
||||
|
@ -27,6 +27,7 @@ import im.vector.app.features.form.formEditableSquareAvatarItem
|
||||
import im.vector.app.features.form.formMultiLineEditTextItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter
|
||||
import org.matrix.android.sdk.api.MatrixConstants
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import javax.inject.Inject
|
||||
@ -94,6 +95,7 @@ class SpaceDetailEpoxyController @Inject constructor(
|
||||
hint(host.stringProvider.getString(R.string.create_space_alias_hint))
|
||||
suffixText(":" + data.homeServerName)
|
||||
prefixText("#")
|
||||
maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName))
|
||||
onFocusChange { hasFocus ->
|
||||
host.aliasTextIsFocused = hasFocus
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user