From 3da5641e2ba738d99f8340a9bca0c64a6cb7ea70 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 22 Sep 2021 16:57:10 +0200 Subject: [PATCH 1/6] Client side validation of alias max length --- changelog.d/3934.bugfix | 1 + .../org/matrix/android/sdk/api/MatrixPatterns.kt | 2 ++ .../im/vector/app/features/form/FormEditTextItem.kt | 13 +++++++++++++ .../createroom/CreateRoomController.kt | 1 + .../roomdirectory/createroom/CreateRoomViewModel.kt | 2 +- .../spaces/create/SpaceDetailEpoxyController.kt | 2 ++ 6 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 changelog.d/3934.bugfix diff --git a/changelog.d/3934.bugfix b/changelog.d/3934.bugfix new file mode 100644 index 00000000000..1de4cba548a --- /dev/null +++ b/changelog.d/3934.bugfix @@ -0,0 +1 @@ +Validate public space address length when user clicks away from \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt index 27936a2b3f2..5134c0d5382 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt @@ -165,6 +165,8 @@ object MatrixPatterns { fun candidateAliasFromRoomName(name: String): String { return Regex("\\s").replace(name.lowercase(), "_").let { "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") + }.let { alias -> + if (alias.length > 255) alias.substring(0, 255) else alias } } diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index 2b2fddd0c99..cda1623c88e 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -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() { @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() { 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 { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index a6799ce7308..70e37b618ba 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -141,6 +141,7 @@ class CreateRoomController @Inject constructor( value(viewState.aliasLocalPart) suffixText(":" + viewState.homeServerName) prefixText("#") + maxLength(255) hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage( host.roomAliasErrorFormatter.format( diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt index 8ddb6d7a094..14789aabb5f 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -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(initialState) { @AssistedFactory diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index 27c08d1f6fb..3789e501c13 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -94,6 +94,8 @@ class SpaceDetailEpoxyController @Inject constructor( hint(host.stringProvider.getString(R.string.create_space_alias_hint)) suffixText(":" + data.homeServerName) prefixText("#") + // spaces alias are limited to 255 + maxLength(255) onFocusChange { hasFocus -> host.aliasTextIsFocused = hasFocus } From c3b65a9c715f0ab93f844f368eea92e7ec887fcc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Oct 2021 12:29:39 +0200 Subject: [PATCH 2/6] Create MatrixConstants to handle max alias length limitation --- .../matrix/android/sdk/api/MatrixConstants.kt | 34 +++++++++++++++++++ .../createroom/CreateRoomController.kt | 3 +- .../createroom/CreateSubSpaceController.kt | 2 ++ .../roomprofile/alias/RoomAliasController.kt | 2 ++ .../spaces/create/CreateSpaceState.kt | 2 +- .../create/SpaceDetailEpoxyController.kt | 4 +-- 6 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt new file mode 100644 index 00000000000..49520f36785 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt @@ -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) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index 70e37b618ba..7dd9cb4e00e 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -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,7 +142,7 @@ class CreateRoomController @Inject constructor( value(viewState.aliasLocalPart) suffixText(":" + viewState.homeServerName) prefixText("#") - maxLength(255) + maxLength(MatrixConstants.maxAliasLocalPartLength(viewState.homeServerName)) hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage( host.roomAliasErrorFormatter.format( diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt index 6d292c85da1..26ea2f30a3b 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index a14bb61606f..206f97f1841 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -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 @@ -253,6 +254,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 -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt index 6fb58532691..ab7fb0cc251 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt @@ -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 = Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index 3789e501c13..14b0db2cd17 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -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,8 +95,7 @@ class SpaceDetailEpoxyController @Inject constructor( hint(host.stringProvider.getString(R.string.create_space_alias_hint)) suffixText(":" + data.homeServerName) prefixText("#") - // spaces alias are limited to 255 - maxLength(255) + maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName)) onFocusChange { hasFocus -> host.aliasTextIsFocused = hasFocus } From 7636b4d7a82c0ac7e4b237f081f4d8eec7ab167e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Oct 2021 12:40:43 +0200 Subject: [PATCH 3/6] limit alias length in candidateAliasFromRoomName() --- .../org/matrix/android/sdk/api/MatrixPatterns.kt | 14 ++++++++------ .../features/spaces/create/CreateSpaceViewModel.kt | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt index 5134c0d5382..034efc26832 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt @@ -162,12 +162,14 @@ 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, "") - }.let { alias -> - if (alias.length > 255) alias.substring(0, 255) else alias - } + fun candidateAliasFromRoomName(roomName: String, domain: String): String { + return roomName.lowercase() + // Replace spaces by '_' + .let { Regex("\\s").replace(it, "_") } + // Remove all invalid chars + .let { "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") } + // limit length + .substring(0, MatrixConstants.maxAliasLocalPartLength(domain)) } /** diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index e6ead2294ef..4495ee31a10 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -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, From 489aedbc37c2b8fc6c720f848f8cab10c4836010 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Oct 2021 12:50:50 +0200 Subject: [PATCH 4/6] Add missing proper configuration for manual publishing of alias --- .../vector/app/features/roomprofile/alias/RoomAliasController.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 206f97f1841..a1c252d3562 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -175,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 -> From ba80bf5ba171d58bbbeba3223ccf52c2e245029c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Oct 2021 12:54:15 +0200 Subject: [PATCH 5/6] Update changelog --- changelog.d/3934.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/3934.bugfix b/changelog.d/3934.bugfix index 1de4cba548a..989f96d004e 100644 --- a/changelog.d/3934.bugfix +++ b/changelog.d/3934.bugfix @@ -1 +1 @@ -Validate public space address length when user clicks away from \ No newline at end of file +Validate public space addresses and room aliases length \ No newline at end of file From f385e7466238c101ae8afbd0ea519eb0f3291652 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Oct 2021 14:13:25 +0200 Subject: [PATCH 6/6] Improve reusability of code --- .../org/matrix/android/sdk/api/MatrixPatterns.kt | 15 +++++++-------- .../android/sdk/internal/util/StringUtils.kt | 4 +++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt index 034efc26832..2a26b612fb0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt @@ -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) } /** @@ -164,12 +166,9 @@ object MatrixPatterns { fun candidateAliasFromRoomName(roomName: String, domain: String): String { return roomName.lowercase() - // Replace spaces by '_' - .let { Regex("\\s").replace(it, "_") } - // Remove all invalid chars - .let { "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") } - // limit length - .substring(0, MatrixConstants.maxAliasLocalPartLength(domain)) + .replaceSpaceChars(replacement = "_") + .removeInvalidRoomNameChars() + .take(MatrixConstants.maxAliasLocalPartLength(domain)) } /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt index aa0b92aa458..8a6ec189863 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt @@ -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, "")