mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-18 05:20:50 +01:00
feat(conversations): add option to force passwords in public conversations
Signed-off-by: Anna Larch <anna@nextcloud.com>
This commit is contained in:
parent
5ca9e8eb9a
commit
d77753d1ce
28 changed files with 337 additions and 40 deletions
|
|
@ -166,3 +166,7 @@
|
|||
* `config => call => start-without-media` (local) - Boolean, whether media should be disabled when starting or joining a conversation
|
||||
* `config => call => max-duration` - Integer, maximum call duration in seconds. Please note that this should only be used with system cron and with a reasonable high value, due to the expended duration until the background job ran.
|
||||
* `config => call => blur-virtual-background` (local) - Boolean, whether blur background is set by default when joining a conversation
|
||||
|
||||
## 21
|
||||
* `config => conversations => force-passwords` - Whether passwords are enforced for public rooms
|
||||
* `conversation-creation-password` - Whether the endpoints for creating public conversations or making a conversation public support setting a password
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@
|
|||
| `roomName` | string | Conversation name up to 255 characters (Not available for `roomType = 1`) |
|
||||
| `objectType` | string | Type of an object this room references, currently only allowed value is `room` to indicate the parent of a breakout room (See [Object types](constants.md#object-types)) |
|
||||
| `objectId` | string | Id of an object this room references, room token is used for the parent of a breakout room |
|
||||
| `password` | string | Password for the room (only available with `conversation-creation-password` capability) |
|
||||
|
||||
* Response:
|
||||
- Status code:
|
||||
|
|
@ -135,6 +136,7 @@
|
|||
+ `201 Created` When the conversation was created
|
||||
+ `400 Bad Request` When an invalid conversation type was given
|
||||
+ `400 Bad Request` When the conversation name is empty for `type = 3`
|
||||
+ `400 Bad Request` When a password is required for a public room or when the password is invalid according to the password policy
|
||||
+ `401 Unauthorized` When the user is not logged in
|
||||
+ `404 Not Found` When the target to invite does not exist
|
||||
|
||||
|
|
@ -283,6 +285,11 @@ Get all (for moderators and in case of "free selection") or the assigned breakou
|
|||
|
||||
* Method: `POST`
|
||||
* Endpoint: `/room/{token}/public`
|
||||
* Data:
|
||||
|
||||
| field | type | Description |
|
||||
|------------|---------|-------------------------------------------------------------------------------------------------|
|
||||
| `password` | ?string | Password for the conversation (only available with `conversation-creation-password` capability) |
|
||||
|
||||
* Response:
|
||||
- Status code:
|
||||
|
|
|
|||
|
|
@ -113,5 +113,6 @@ Legend:
|
|||
| `conversations_files` | string<br>`1` or `0` | `1` | No | 🖌️ | Whether the files app integration is enabled allowing to start conversations in the right sidebar |
|
||||
| `conversations_files_public_shares` | string<br>`1` or `0` | `1` | No | 🖌️ | Whether the public share integration is enabled allowing to start conversations in the right sidebar on the public share page (Requires `conversations_files` also to be enabled) |
|
||||
| `enable_matterbridge` | string<br>`1` or `0` | `0` | No | 🖌️ | Whether the Matterbridge integration is enabled and can be configured |
|
||||
| `force_passwords` | string<br>`1` or `0` | `0` | No | ️ | Whether public chats are forced to use a password |
|
||||
| `inactivity_lock_after_days` | int | `0` | No | | A duration (in days) after which rooms are locked. Calculated from the last activity in the room. |
|
||||
| `inactivity_enable_lobby` | string<br>`1` or `0` | `0` | No | | Additionally enable the lobby for inactive rooms so they can only be read by moderators. |
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ class Capabilities implements IPublicCapability {
|
|||
'talk-polls-drafts',
|
||||
'download-call-participants',
|
||||
'email-csv-import',
|
||||
'conversation-creation-password',
|
||||
];
|
||||
|
||||
public const CONDITIONAL_FEATURES = [
|
||||
|
|
@ -224,7 +225,8 @@ class Capabilities implements IPublicCapability {
|
|||
'summary-threshold' => 100,
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create' => $user instanceof IUser && !$this->talkConfig->isNotAllowedToCreateConversations($user)
|
||||
'can-create' => $user instanceof IUser && !$this->talkConfig->isNotAllowedToCreateConversations($user),
|
||||
'force-passwords' => $this->talkConfig->isPasswordEnforced(),
|
||||
],
|
||||
'federation' => [
|
||||
'enabled' => false,
|
||||
|
|
|
|||
|
|
@ -705,4 +705,8 @@ class Config {
|
|||
public function enableLobbyOnLockedRooms(): bool {
|
||||
return $this->appConfig->getAppValueBool('inactivity_enable_lobby');
|
||||
}
|
||||
|
||||
public function isPasswordEnforced(): bool {
|
||||
return $this->appConfig->getAppValueBool('force_passwords');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -502,16 +502,25 @@ class RoomController extends AEnvironmentAwareController {
|
|||
* @param 'groups'|'circles'|'' $source Source of the invite ID ('circles' to create a room with a circle, etc.)
|
||||
* @param string $objectType Type of the object
|
||||
* @param string $objectId ID of the object
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_CREATED, TalkRoom, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error?: string}, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>
|
||||
* @param string $password The room password (only available with `conversation-creation-password` capability)
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_CREATED, TalkRoom, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error?: string, message?: string}, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>
|
||||
*
|
||||
* 200: Room already existed
|
||||
* 201: Room created successfully
|
||||
* 400: Room type invalid
|
||||
* 400: Room type invalid or missing or invalid password
|
||||
* 403: Missing permissions to create room
|
||||
* 404: User, group or other target to invite was not found
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
public function createRoom(int $roomType, string $invite = '', string $roomName = '', string $source = '', string $objectType = '', string $objectId = ''): DataResponse {
|
||||
public function createRoom(
|
||||
int $roomType,
|
||||
string $invite = '',
|
||||
string $roomName = '',
|
||||
string $source = '',
|
||||
string $objectType = '',
|
||||
string $objectId = '',
|
||||
string $password = '',
|
||||
): DataResponse {
|
||||
if ($roomType !== Room::TYPE_ONE_TO_ONE) {
|
||||
/** @var IUser $user */
|
||||
$user = $this->userManager->get($this->userId);
|
||||
|
|
@ -533,7 +542,7 @@ class RoomController extends AEnvironmentAwareController {
|
|||
}
|
||||
return $this->createGroupRoom($invite);
|
||||
case Room::TYPE_PUBLIC:
|
||||
return $this->createEmptyRoom($roomName, true, $objectType, $objectId);
|
||||
return $this->createEmptyRoom($roomName, true, $objectType, $objectId, $password);
|
||||
}
|
||||
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
|
|
@ -645,10 +654,10 @@ class RoomController extends AEnvironmentAwareController {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_CREATED, TalkRoom, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error?: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
|
||||
* @return DataResponse<Http::STATUS_CREATED, TalkRoom, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error?: string, message?: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
protected function createEmptyRoom(string $roomName, bool $public = true, string $objectType = '', string $objectId = ''): DataResponse {
|
||||
protected function createEmptyRoom(string $roomName, bool $public = true, string $objectType = '', string $objectId = '', string $password = ''): DataResponse {
|
||||
$currentUser = $this->userManager->get($this->userId);
|
||||
if (!$currentUser instanceof IUser) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
|
|
@ -686,7 +695,9 @@ class RoomController extends AEnvironmentAwareController {
|
|||
|
||||
// Create the room
|
||||
try {
|
||||
$room = $this->roomService->createConversation($roomType, $roomName, $currentUser, $objectType, $objectId);
|
||||
$room = $this->roomService->createConversation($roomType, $roomName, $currentUser, $objectType, $objectId, $password);
|
||||
} catch (PasswordException $e) {
|
||||
return new DataResponse(['error' => 'password', 'message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
|
@ -1420,16 +1431,29 @@ class RoomController extends AEnvironmentAwareController {
|
|||
/**
|
||||
* Allowed guests to join conversation
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'}, array{}>
|
||||
* Required capability: `conversation-creation-password` for `string $password` parameter
|
||||
*
|
||||
* @param string $password New password (only available with `conversation-creation-password` capability)
|
||||
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'breakout-room'|'type'|'value'|'password', message?: null|string}, array{}>
|
||||
*
|
||||
* 200: Allowed guests successfully
|
||||
* 400: Allowing guests is not possible
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
#[RequireLoggedInModeratorParticipant]
|
||||
public function makePublic(): DataResponse {
|
||||
public function makePublic(string $password = ''): DataResponse {
|
||||
if ($this->talkConfig->isPasswordEnforced() && $password === '') {
|
||||
return new DataResponse(['error' => 'password', 'message' => $this->l->t('Password needs to be set')], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->roomService->setType($this->room, Room::TYPE_PUBLIC);
|
||||
if ($password !== '') {
|
||||
$this->roomService->makePublicWithPassword($this->room, $password);
|
||||
} else {
|
||||
$this->roomService->setType($this->room, Room::TYPE_PUBLIC);
|
||||
}
|
||||
} catch (PasswordException $e) {
|
||||
return new DataResponse(['error' => 'password', 'message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
|
||||
} catch (TypeException $e) {
|
||||
return new DataResponse(['error' => $e->getReason()], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
|
|||
use OCP\Comments\IComment;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\Comments\NotFoundException;
|
||||
use OCP\DB\Exception;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ICache;
|
||||
|
|
@ -1108,7 +1109,7 @@ class Manager {
|
|||
* @param string $objectId
|
||||
* @return Room
|
||||
*/
|
||||
public function createRoom(int $type, string $name = '', string $objectType = '', string $objectId = ''): Room {
|
||||
public function createRoom(int $type, string $name = '', string $objectType = '', string $objectId = '', string $password = ''): Room {
|
||||
$token = $this->getNewToken();
|
||||
|
||||
$insert = $this->db->getQueryBuilder();
|
||||
|
|
@ -1118,6 +1119,7 @@ class Manager {
|
|||
'name' => $insert->createNamedParameter($name),
|
||||
'type' => $insert->createNamedParameter($type, IQueryBuilder::PARAM_INT),
|
||||
'token' => $insert->createNamedParameter($token),
|
||||
'password' => $insert->createNamedParameter($password),
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -1135,6 +1137,7 @@ class Manager {
|
|||
'token' => $token,
|
||||
'object_type' => $objectType,
|
||||
'object_id' => $objectId,
|
||||
'password' => $password
|
||||
]);
|
||||
|
||||
$event = new RoomCreatedEvent($room);
|
||||
|
|
@ -1409,4 +1412,18 @@ class Manager {
|
|||
$query->selectAlias('c.expire_date', 'comment_expire_date');
|
||||
$query->selectAlias('c.meta_data', 'comment_meta_data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $roomId
|
||||
* @param string $password
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setPublic(int $roomId, string $password = ''): void {
|
||||
$update = $this->db->getQueryBuilder();
|
||||
$update->update('talk_rooms')
|
||||
->set('type', $update->createNamedParameter(Room::TYPE_PUBLIC, IQueryBuilder::PARAM_INT))
|
||||
->set('password', $update->createNamedParameter($password, IQueryBuilder::PARAM_STR))
|
||||
->where($update->expr()->eq('id', $update->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
$update->executeStatement();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ namespace OCA\Talk;
|
|||
* },
|
||||
* conversations: array{
|
||||
* can-create: bool,
|
||||
* force-passwords: bool,
|
||||
* },
|
||||
* federation: array{
|
||||
* enabled: bool,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
|
|||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\HintException;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IL10N;
|
||||
use OCP\IUser;
|
||||
use OCP\Log\Audit\CriticalActionPerformedEvent;
|
||||
use OCP\Security\Events\ValidatePasswordPolicyEvent;
|
||||
|
|
@ -80,6 +81,7 @@ class RoomService {
|
|||
protected IEventDispatcher $dispatcher,
|
||||
protected IJobList $jobList,
|
||||
protected LoggerInterface $logger,
|
||||
protected IL10N $l10n,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -127,17 +129,13 @@ class RoomService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @param string $name
|
||||
* @param IUser|null $owner
|
||||
* @param string $objectType
|
||||
* @param string $objectId
|
||||
* @return Room
|
||||
* @throws InvalidArgumentException on too long or empty names
|
||||
* @throws InvalidArgumentException unsupported type
|
||||
* @throws InvalidArgumentException invalid object data
|
||||
* @throws PasswordException empty or invalid password
|
||||
*/
|
||||
public function createConversation(int $type, string $name, ?IUser $owner = null, string $objectType = '', string $objectId = ''): Room {
|
||||
public function createConversation(int $type, string $name, ?IUser $owner = null, string $objectType = '', string $objectId = '', string $password = ''): Room {
|
||||
$name = trim($name);
|
||||
if ($name === '' || mb_strlen($name) > 255) {
|
||||
throw new InvalidArgumentException('name');
|
||||
|
|
@ -167,7 +165,20 @@ class RoomService {
|
|||
throw new InvalidArgumentException('object');
|
||||
}
|
||||
|
||||
$room = $this->manager->createRoom($type, $name, $objectType, $objectId);
|
||||
if ($type !== Room::TYPE_PUBLIC || !$this->config->isPasswordEnforced()) {
|
||||
$room = $this->manager->createRoom($type, $name, $objectType, $objectId);
|
||||
} elseif ($password === '') {
|
||||
throw new PasswordException(PasswordException::REASON_VALUE, $this->l10n->t('Password needs to be set'));
|
||||
} else {
|
||||
$event = new ValidatePasswordPolicyEvent($password);
|
||||
try {
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
} catch (HintException $e) {
|
||||
throw new PasswordException(PasswordException::REASON_VALUE, $e->getHint());
|
||||
}
|
||||
$passwordHash = $this->hasher->hash($password);
|
||||
$room = $this->manager->createRoom($type, $name, $objectType, $objectId, $passwordHash);
|
||||
}
|
||||
|
||||
if ($owner instanceof IUser) {
|
||||
$this->participantService->addUsers($room, [[
|
||||
|
|
@ -177,8 +188,8 @@ class RoomService {
|
|||
'participantType' => Participant::OWNER,
|
||||
]], null);
|
||||
}
|
||||
|
||||
return $room;
|
||||
|
||||
}
|
||||
|
||||
public function prepareConversationName(string $objectName): string {
|
||||
|
|
@ -545,6 +556,44 @@ class RoomService {
|
|||
$this->dispatcher->dispatchTyped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PasswordException|TypeException
|
||||
*/
|
||||
public function makePublicWithPassword(Room $room, string $password): void {
|
||||
if ($room->getType() === Room::TYPE_PUBLIC) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($room->getType() !== Room::TYPE_GROUP) {
|
||||
throw new TypeException(TypeException::REASON_TYPE);
|
||||
}
|
||||
|
||||
if ($password === '') {
|
||||
throw new PasswordException(PasswordException::REASON_VALUE, $this->l10n->t('Password needs to be set'));
|
||||
}
|
||||
|
||||
$event = new ValidatePasswordPolicyEvent($password);
|
||||
try {
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
} catch (HintException $e) {
|
||||
throw new PasswordException(PasswordException::REASON_VALUE, $e->getHint());
|
||||
}
|
||||
|
||||
$event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_TYPE, Room::TYPE_PUBLIC, $room->getType());
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
$event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_PASSWORD, $password);
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
|
||||
$passwordHash = $this->hasher->hash($password);
|
||||
$this->manager->setPublic($room->getId(), $passwordHash);
|
||||
$room->setType(Room::TYPE_PUBLIC);
|
||||
|
||||
$event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_TYPE, Room::TYPE_PUBLIC, $room->getType());
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
$event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_PASSWORD, $password);
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Room $room
|
||||
* @param int $newState Currently it is only allowed to change between
|
||||
|
|
@ -1221,4 +1270,42 @@ class RoomService {
|
|||
public function getInactiveRooms(\DateTime $inactiveSince): array {
|
||||
return $this->manager->getInactiveRooms($inactiveSince);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Room $room
|
||||
* @param int $oldType
|
||||
* @param int $newType
|
||||
* @param bool $allowSwitchingOneToOne
|
||||
* @return void
|
||||
*/
|
||||
public function validateRoomTypeSwitch(Room $room, int $oldType, int $newType, bool $allowSwitchingOneToOne): void {
|
||||
if (!$allowSwitchingOneToOne && $oldType === Room::TYPE_ONE_TO_ONE) {
|
||||
throw new TypeException(TypeException::REASON_TYPE);
|
||||
}
|
||||
|
||||
if ($oldType === Room::TYPE_ONE_TO_ONE_FORMER) {
|
||||
throw new TypeException(TypeException::REASON_TYPE);
|
||||
}
|
||||
|
||||
if ($oldType === Room::TYPE_NOTE_TO_SELF) {
|
||||
throw new TypeException(TypeException::REASON_TYPE);
|
||||
}
|
||||
|
||||
if (!in_array($newType, [Room::TYPE_GROUP, Room::TYPE_PUBLIC, Room::TYPE_ONE_TO_ONE_FORMER], true)) {
|
||||
throw new TypeException(TypeException::REASON_VALUE);
|
||||
}
|
||||
|
||||
if ($newType === Room::TYPE_ONE_TO_ONE_FORMER && $oldType !== Room::TYPE_ONE_TO_ONE) {
|
||||
throw new TypeException(TypeException::REASON_VALUE);
|
||||
}
|
||||
|
||||
if ($room->getBreakoutRoomMode() !== BreakoutRoom::MODE_NOT_CONFIGURED) {
|
||||
throw new TypeException(TypeException::REASON_BREAKOUT_ROOM);
|
||||
}
|
||||
|
||||
if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) {
|
||||
throw new TypeException(TypeException::REASON_BREAKOUT_ROOM);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,11 +237,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -170,11 +170,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -170,11 +170,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -213,11 +213,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -170,11 +170,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -213,11 +213,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -389,11 +389,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -11252,6 +11256,11 @@
|
|||
"type": "string",
|
||||
"default": "",
|
||||
"description": "ID of the object"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The room password (only available with `conversation-creation-password` capability)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11344,7 +11353,7 @@
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Room type invalid",
|
||||
"description": "Room type invalid or missing or invalid password",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -11368,6 +11377,9 @@
|
|||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11882,6 +11894,7 @@
|
|||
"post": {
|
||||
"operationId": "room-make-public",
|
||||
"summary": "Allowed guests to join conversation",
|
||||
"description": "Required capability: `conversation-creation-password` for `string $password` parameter",
|
||||
"tags": [
|
||||
"room"
|
||||
],
|
||||
|
|
@ -11893,6 +11906,23 @@
|
|||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": false,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "New password (only available with `conversation-creation-password` capability)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apiVersion",
|
||||
|
|
@ -11986,8 +12016,13 @@
|
|||
"enum": [
|
||||
"breakout-room",
|
||||
"type",
|
||||
"value"
|
||||
"value",
|
||||
"password"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
41
openapi.json
41
openapi.json
|
|
@ -330,11 +330,15 @@
|
|||
"conversations": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"can-create"
|
||||
"can-create",
|
||||
"force-passwords"
|
||||
],
|
||||
"properties": {
|
||||
"can-create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"force-passwords": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -11139,6 +11143,11 @@
|
|||
"type": "string",
|
||||
"default": "",
|
||||
"description": "ID of the object"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The room password (only available with `conversation-creation-password` capability)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11231,7 +11240,7 @@
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Room type invalid",
|
||||
"description": "Room type invalid or missing or invalid password",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -11255,6 +11264,9 @@
|
|||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12016,6 +12028,7 @@
|
|||
"post": {
|
||||
"operationId": "room-make-public",
|
||||
"summary": "Allowed guests to join conversation",
|
||||
"description": "Required capability: `conversation-creation-password` for `string $password` parameter",
|
||||
"tags": [
|
||||
"room"
|
||||
],
|
||||
|
|
@ -12027,6 +12040,23 @@
|
|||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": false,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "New password (only available with `conversation-creation-password` capability)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apiVersion",
|
||||
|
|
@ -12120,8 +12150,13 @@
|
|||
"enum": [
|
||||
"breakout-room",
|
||||
"type",
|
||||
"value"
|
||||
"value",
|
||||
"password"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ export const mockedCapabilities: Capabilities = {
|
|||
},
|
||||
conversations: {
|
||||
'can-create': true,
|
||||
'force-passwords': false,
|
||||
},
|
||||
federation: {
|
||||
enabled: false,
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -855,7 +855,10 @@ export type paths = {
|
|||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Allowed guests to join conversation */
|
||||
/**
|
||||
* Allowed guests to join conversation
|
||||
* @description Required capability: `conversation-creation-password` for `string $password` parameter
|
||||
*/
|
||||
post: operations["room-make-public"];
|
||||
/** Disallowed guests to join conversation */
|
||||
delete: operations["room-make-private"];
|
||||
|
|
@ -1989,6 +1992,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
@ -6215,6 +6219,11 @@ export interface operations {
|
|||
* @default
|
||||
*/
|
||||
objectId?: string;
|
||||
/**
|
||||
* @description The room password (only available with `conversation-creation-password` capability)
|
||||
* @default
|
||||
*/
|
||||
password?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -6247,7 +6256,7 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
/** @description Room type invalid */
|
||||
/** @description Room type invalid or missing or invalid password */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
|
|
@ -6258,6 +6267,7 @@ export interface operations {
|
|||
meta: components["schemas"]["OCSMeta"];
|
||||
data: {
|
||||
error?: string;
|
||||
message?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -6478,7 +6488,17 @@ export interface operations {
|
|||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
requestBody?: {
|
||||
content: {
|
||||
"application/json": {
|
||||
/**
|
||||
* @description New password (only available with `conversation-creation-password` capability)
|
||||
* @default
|
||||
*/
|
||||
password?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Allowed guests successfully */
|
||||
200: {
|
||||
|
|
@ -6505,7 +6525,8 @@ export interface operations {
|
|||
meta: components["schemas"]["OCSMeta"];
|
||||
data: {
|
||||
/** @enum {string} */
|
||||
error: "breakout-room" | "type" | "value";
|
||||
error: "breakout-room" | "type" | "value" | "password";
|
||||
message?: string | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -857,7 +857,10 @@ export type paths = {
|
|||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Allowed guests to join conversation */
|
||||
/**
|
||||
* Allowed guests to join conversation
|
||||
* @description Required capability: `conversation-creation-password` for `string $password` parameter
|
||||
*/
|
||||
post: operations["room-make-public"];
|
||||
/** Disallowed guests to join conversation */
|
||||
delete: operations["room-make-private"];
|
||||
|
|
@ -1486,6 +1489,7 @@ export type components = {
|
|||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
"force-passwords": boolean;
|
||||
};
|
||||
federation: {
|
||||
enabled: boolean;
|
||||
|
|
@ -5696,6 +5700,11 @@ export interface operations {
|
|||
* @default
|
||||
*/
|
||||
objectId?: string;
|
||||
/**
|
||||
* @description The room password (only available with `conversation-creation-password` capability)
|
||||
* @default
|
||||
*/
|
||||
password?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -5728,7 +5737,7 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
/** @description Room type invalid */
|
||||
/** @description Room type invalid or missing or invalid password */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
|
|
@ -5739,6 +5748,7 @@ export interface operations {
|
|||
meta: components["schemas"]["OCSMeta"];
|
||||
data: {
|
||||
error?: string;
|
||||
message?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -6059,7 +6069,17 @@ export interface operations {
|
|||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
requestBody?: {
|
||||
content: {
|
||||
"application/json": {
|
||||
/**
|
||||
* @description New password (only available with `conversation-creation-password` capability)
|
||||
* @default
|
||||
*/
|
||||
password?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Allowed guests successfully */
|
||||
200: {
|
||||
|
|
@ -6086,7 +6106,8 @@ export interface operations {
|
|||
meta: components["schemas"]["OCSMeta"];
|
||||
data: {
|
||||
/** @enum {string} */
|
||||
error: "breakout-room" | "type" | "value";
|
||||
error: "breakout-room" | "type" | "value" | "password";
|
||||
message?: string | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ class CapabilitiesTest extends TestCase {
|
|||
],
|
||||
'conversations' => [
|
||||
'can-create' => false,
|
||||
'force-passwords' => false,
|
||||
],
|
||||
'federation' => [
|
||||
'enabled' => false,
|
||||
|
|
@ -284,6 +285,7 @@ class CapabilitiesTest extends TestCase {
|
|||
],
|
||||
'conversations' => [
|
||||
'can-create' => $canCreate,
|
||||
'force-passwords' => false,
|
||||
],
|
||||
'federation' => [
|
||||
'enabled' => false,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
|
|||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IL10N;
|
||||
use OCP\IUser;
|
||||
use OCP\Security\IHasher;
|
||||
use OCP\Share\IManager as IShareManager;
|
||||
|
|
@ -46,6 +47,7 @@ class RoomServiceTest extends TestCase {
|
|||
protected IEventDispatcher&MockObject $dispatcher;
|
||||
protected IJobList&MockObject $jobList;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected IL10N&MockObject $l10n;
|
||||
protected ?RoomService $service = null;
|
||||
|
||||
public function setUp(): void {
|
||||
|
|
@ -60,6 +62,7 @@ class RoomServiceTest extends TestCase {
|
|||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->jobList = $this->createMock(IJobList::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->service = new RoomService(
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
|
|
@ -71,6 +74,7 @@ class RoomServiceTest extends TestCase {
|
|||
$this->dispatcher,
|
||||
$this->jobList,
|
||||
$this->logger,
|
||||
$this->l10n,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -332,6 +336,7 @@ class RoomServiceTest extends TestCase {
|
|||
$dispatcher,
|
||||
$this->jobList,
|
||||
$this->logger,
|
||||
$this->l10n,
|
||||
);
|
||||
|
||||
$room = new Room(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue