Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2022-06-30 10:20:10 +0300
committerGitHub <noreply@github.com>2022-06-30 10:20:10 +0300
commit57b28f64d6b02a7879da51e7c264a87472e94ed8 (patch)
treee10748717b5f037be4b65e29f613179b9bef74a3
parentd0d015820077fd8d8d559474f8b2d1ac9196421f (diff)
parent5fdedaa290eb8ba1801373ca04a7953af8e8711b (diff)
Merge pull request #7473 from nextcloud/feature/3089/password-policy
Implement password policy checker
-rw-r--r--docs/conversation.md6
-rw-r--r--lib/Command/Room/TRoomCommand.php9
-rw-r--r--lib/Controller/RoomController.php10
-rw-r--r--lib/Room.php30
-rw-r--r--lib/Service/RoomService.php35
-rw-r--r--src/components/ConversationSettings/LinkShareSettings.vue10
-rw-r--r--src/components/LeftSidebar/NewGroupConversation/PasswordProtect/PasswordProtect.vue43
-rw-r--r--src/services/conversationsService.js7
-rw-r--r--tests/php/Signaling/BackendNotifierTest.php2
9 files changed, 119 insertions, 33 deletions
diff --git a/docs/conversation.md b/docs/conversation.md
index 4956578e2..54a6b7ed6 100644
--- a/docs/conversation.md
+++ b/docs/conversation.md
@@ -247,10 +247,16 @@
* Response:
- Status code:
+ `200 OK`
+ + `400 Bad Request` When the password does not match the password policy. Show `ocs.data.message` to the user in this case
+ `403 Forbidden` When the current user is not a moderator or owner
+ `403 Forbidden` When the conversation is not a public conversation
+ `404 Not Found` When the conversation could not be found for the participant
+ - Data:
+ field | type | Description
+ ---|---|---
+ `message` | string | Only available on `400 Bad Request`, translated error with the violated password policy rules
+
## Set default or call permissions for a conversation
* Method: `PUT`
diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php
index 280f96b8d..157682cf0 100644
--- a/lib/Command/Room/TRoomCommand.php
+++ b/lib/Command/Room/TRoomCommand.php
@@ -35,6 +35,7 @@ use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\RoomService;
+use OCP\HintException;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
@@ -183,8 +184,12 @@ trait TRoomCommand {
throw new InvalidArgumentException('Unable to add password protection to private room.');
}
- if (!$room->setPassword($password)) {
- throw new InvalidArgumentException('Unable to change room password.');
+ try {
+ if (!$this->roomService->setPassword($room, $password)) {
+ throw new InvalidArgumentException('Unable to change room password.');
+ }
+ } catch (HintException $e) {
+ throw new InvalidArgumentException($e->getHint());
}
}
diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php
index 3d5e542ed..40c457de3 100644
--- a/lib/Controller/RoomController.php
+++ b/lib/Controller/RoomController.php
@@ -56,6 +56,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudIdManager;
+use OCP\HintException;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
@@ -1315,7 +1316,14 @@ class RoomController extends AEnvironmentAwareController {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
- $this->room->setPassword($password);
+ try {
+ $this->roomService->setPassword($this->room, $password);
+ } catch (HintException $e) {
+ return new DataResponse([
+ 'message' => $e->getHint(),
+ ], Http::STATUS_BAD_REQUEST);
+ }
+
return new DataResponse();
}
diff --git a/lib/Room.php b/lib/Room.php
index a965dda8c..e022c938f 100644
--- a/lib/Room.php
+++ b/lib/Room.php
@@ -426,6 +426,10 @@ class Room {
return $this->password;
}
+ public function setPassword(string $password): void {
+ $this->password = $password;
+ }
+
public function getRemoteServer(): string {
return $this->remoteServer;
}
@@ -693,32 +697,6 @@ class Room {
}
/**
- * @param string $password Currently it is only allowed to have a password for Room::TYPE_PUBLIC
- * @return bool True when the change was valid, false otherwise
- */
- public function setPassword(string $password): bool {
- if ($this->getType() !== self::TYPE_PUBLIC) {
- return false;
- }
-
- $hash = $password !== '' ? $this->hasher->hash($password) : '';
-
- $event = new ModifyRoomEvent($this, 'password', $password);
- $this->dispatcher->dispatch(self::EVENT_BEFORE_PASSWORD_SET, $event);
-
- $update = $this->db->getQueryBuilder();
- $update->update('talk_rooms')
- ->set('password', $update->createNamedParameter($hash))
- ->where($update->expr()->eq('id', $update->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT)));
- $update->executeStatement();
- $this->password = $hash;
-
- $this->dispatcher->dispatch(self::EVENT_AFTER_PASSWORD_SET, $event);
-
- return true;
- }
-
- /**
* @param \DateTime $now
* @return bool
*/
diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php
index ca5f8cb8a..f33aeab12 100644
--- a/lib/Service/RoomService.php
+++ b/lib/Service/RoomService.php
@@ -35,8 +35,10 @@ use OCA\Talk\Room;
use OCA\Talk\Webinary;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\HintException;
use OCP\IDBConnection;
use OCP\IUser;
+use OCP\Security\Events\ValidatePasswordPolicyEvent;
use OCP\Security\IHasher;
use OCP\Share\IManager as IShareManager;
@@ -473,6 +475,39 @@ class RoomService {
return true;
}
+ /**
+ * @param string $password Currently it is only allowed to have a password for Room::TYPE_PUBLIC
+ * @return bool True when the change was valid, false otherwise
+ * @throws HintException
+ */
+ public function setPassword(Room $room, string $password): bool {
+ if ($room->getType() !== Room::TYPE_PUBLIC) {
+ return false;
+ }
+
+ if ($password !== '') {
+ $event = new ValidatePasswordPolicyEvent($password);
+ $this->dispatcher->dispatchTyped($event);
+ }
+
+ $hash = $password !== '' ? $this->hasher->hash($password) : '';
+
+ $event = new ModifyRoomEvent($room, 'password', $password);
+ $this->dispatcher->dispatch(Room::EVENT_BEFORE_PASSWORD_SET, $event);
+
+ $update = $this->db->getQueryBuilder();
+ $update->update('talk_rooms')
+ ->set('password', $update->createNamedParameter($hash))
+ ->where($update->expr()->eq('id', $update->createNamedParameter($room->getId(), IQueryBuilder::PARAM_INT)));
+ $update->executeStatement();
+
+ $room->setPassword($hash);
+
+ $this->dispatcher->dispatch(Room::EVENT_AFTER_PASSWORD_SET, $event);
+
+ return true;
+ }
+
public function verifyPassword(Room $room, string $password): array {
$event = new VerifyRoomPasswordEvent($room, $password);
$this->dispatcher->dispatch(Room::EVENT_PASSWORD_VERIFY, $event);
diff --git a/src/components/ConversationSettings/LinkShareSettings.vue b/src/components/ConversationSettings/LinkShareSettings.vue
index 8b53cb0d6..14706ed41 100644
--- a/src/components/ConversationSettings/LinkShareSettings.vue
+++ b/src/components/ConversationSettings/LinkShareSettings.vue
@@ -163,9 +163,13 @@ export default {
} else {
showSuccess(t('spreed', 'Conversation password has been removed'))
}
- } catch (e) {
- console.error('Error saving conversation password', e)
- showError(t('spreed', 'Error occurred while saving conversation password'))
+ } catch (error) {
+ console.error('Error saving conversation password', error)
+ if (error?.response?.data?.ocs?.data?.message) {
+ showError(error.response.data.ocs.data.message)
+ } else {
+ showError(t('spreed', 'Error occurred while saving conversation password'))
+ }
}
this.isSaving = false
},
diff --git a/src/components/LeftSidebar/NewGroupConversation/PasswordProtect/PasswordProtect.vue b/src/components/LeftSidebar/NewGroupConversation/PasswordProtect/PasswordProtect.vue
index c2142988a..59e193fb3 100644
--- a/src/components/LeftSidebar/NewGroupConversation/PasswordProtect/PasswordProtect.vue
+++ b/src/components/LeftSidebar/NewGroupConversation/PasswordProtect/PasswordProtect.vue
@@ -22,16 +22,22 @@
<template>
<input ref="password"
v-observe-visibility="visibilityChanged"
+ v-tooltip.bottom="reason"
type="password"
autocomplete="new-password"
:value="value"
class="password-protect"
+ :class="{'weak-password': validPassword === false}"
:placeholder="t('spreed', 'Choose a password')"
+ :aria-label="reason"
@input="handleInput">
</template>
<script>
+import { validatePassword } from '../../../../services/conversationsService.js'
+import debounce from 'debounce'
+
export default {
name: 'PasswordProtect',
@@ -42,10 +48,41 @@ export default {
},
},
+ data() {
+ return {
+ validPassword: null,
+ reason: '',
+ }
+ },
+
methods: {
handleInput(event) {
this.$emit('input', event.target.value)
+ if (event.target.value !== '') {
+ this.debounceValidatePassword()
+ }
+ },
+
+ debounceValidatePassword: debounce(function() {
+ this.validatePassword()
+ }, 250),
+
+ async validatePassword() {
+ try {
+ const response = await validatePassword(this.value)
+ this.validPassword = response.data.ocs.data.passed
+ if (!this.validPassword) {
+ this.reason = response.data.ocs.data.reason
+ } else {
+ this.reason = ''
+ }
+ } catch (e) {
+ console.debug('Password policy app seems not enabled')
+ this.validPassword = null
+ this.reason = ''
+ }
},
+
visibilityChanged(isVisible) {
if (isVisible) {
// Focus the input field of the current component.
@@ -63,5 +100,11 @@ export default {
.password-protect {
width: calc(100% - 18px);
margin-left: 18px;
+
+ &.weak-password {
+ background-color: var(--color-error) !important;
+ border-color: var(--color-error) !important;
+ color: #fff !important;
+ }
}
</style>
diff --git a/src/services/conversationsService.js b/src/services/conversationsService.js
index 7c01e88a6..bf9b3e7d1 100644
--- a/src/services/conversationsService.js
+++ b/src/services/conversationsService.js
@@ -390,6 +390,12 @@ const setCallPermissions = async (token, permissions) => {
})
}
+const validatePassword = async (password) => {
+ return await axios.post(generateOcsUrl('apps/password_policy/api/v1/validate'), {
+ password,
+ })
+}
+
export {
fetchConversations,
fetchConversation,
@@ -416,4 +422,5 @@ export {
clearConversationHistory,
setConversationPermissions,
setCallPermissions,
+ validatePassword,
}
diff --git a/tests/php/Signaling/BackendNotifierTest.php b/tests/php/Signaling/BackendNotifierTest.php
index 7ba8c4e48..051188a53 100644
--- a/tests/php/Signaling/BackendNotifierTest.php
+++ b/tests/php/Signaling/BackendNotifierTest.php
@@ -462,7 +462,7 @@ class BackendNotifierTest extends TestCase {
public function testRoomPasswordChanged() {
$room = $this->manager->createRoom(Room::TYPE_PUBLIC);
- $room->setPassword('password');
+ $this->roomService->setPassword($room, 'password');
$this->assertMessageWasSent($room, [
'type' => 'update',