Skip to content
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
6 changes: 6 additions & 0 deletions docs/conversation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
9 changes: 7 additions & 2 deletions lib/Command/Room/TRoomCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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;
Expand Down Expand Up @@ -183,8 +184,12 @@ protected function setRoomPassword(Room $room, string $password): void {
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());
}
}

Expand Down
10 changes: 9 additions & 1 deletion lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
use OCP\Comments\IComment;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudIdManager;
use OCP\HintException;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
Expand Down Expand Up @@ -1315,7 +1316,14 @@ public function setPassword(string $password): DataResponse {
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();
}

Expand Down
30 changes: 4 additions & 26 deletions lib/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,10 @@ public function getPassword(): string {
return $this->password;
}

public function setPassword(string $password): void {
$this->password = $password;
}

public function getRemoteServer(): string {
return $this->remoteServer;
}
Expand Down Expand Up @@ -692,32 +696,6 @@ public function setName(string $newName, ?string $oldName = null): bool {
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
*/
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
Expand Down
35 changes: 35 additions & 0 deletions lib/Service/RoomService.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
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;

Expand Down Expand Up @@ -473,6 +475,39 @@ public function setDescription(Room $room, string $description): bool {
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);
Expand Down
10 changes: 7 additions & 3 deletions src/components/ConversationSettings/LinkShareSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',

Expand All @@ -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.
Expand All @@ -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>
7 changes: 7 additions & 0 deletions src/services/conversationsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -416,4 +422,5 @@ export {
clearConversationHistory,
setConversationPermissions,
setCallPermissions,
validatePassword,
}
2 changes: 1 addition & 1 deletion tests/php/Signaling/BackendNotifierTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ public function testRoomDescriptionChanged() {

public function testRoomPasswordChanged() {
$room = $this->manager->createRoom(Room::TYPE_PUBLIC);
$room->setPassword('password');
$this->roomService->setPassword($room, 'password');

$this->assertMessageWasSent($room, [
'type' => 'update',
Expand Down