Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ object BreakoutRoomsUtil extends SystemConfiguration {
breakoutMeetingId: String,
avatarURL: String,
webcamBackgroundURL: String,
role: String,
password: String
role: String
): (collection.immutable.Map[String, String], collection.immutable.Map[String, String]) = {
val moderator = role == "MODERATOR"
val params = collection.immutable.HashMap(
Expand All @@ -83,7 +82,7 @@ object BreakoutRoomsUtil extends SystemConfiguration {
"avatarURL" -> urlEncode(avatarURL),
"webcamBackgroundURL" -> urlEncode(webcamBackgroundURL),
"userdata-bbb_parent_room_moderator" -> urlEncode(moderator.toString()),
"password" -> urlEncode(password),
"role" -> urlEncode(role),
"redirect" -> urlEncode("true")
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ BreakoutRoomUsersUpdateInternalMsg }
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus }
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
import org.bigbluebutton.core.models.{ Roles, Users2x, VoiceUsers }
import org.bigbluebutton.core.models.{ Users2x, VoiceUsers }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }

object BreakoutHdlrHelpers extends SystemConfiguration {
Expand Down Expand Up @@ -41,17 +41,16 @@ object BreakoutHdlrHelpers extends SystemConfiguration {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
apiCall = "join"
password = if (user.role == Roles.MODERATOR_ROLE) liveMeeting.props.password.moderatorPass
else liveMeeting.props.password.viewerPass
// The breakout room is joined using the user's role (MODERATOR/VIEWER);
// the legacy password-based join was removed in BigBlueButton 4.0.
(redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(
user.name,
userId + "-" + roomSequence,
true,
externalMeetingId,
user.avatar,
user.webcamBackground,
user.role,
password
user.role
)
// We generate a first url with redirect -> true
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ class BreakoutRoomsUtilSpec extends UnitSpec {
}

it should "create a base string" in {
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&redirect=true&role=MODERATOR"

val params = new collection.mutable.HashMap[String, String]
params += "fullName" -> BreakoutRoomsUtil.urlEncode("User 4621018")
params += "isBreakout" -> BreakoutRoomsUtil.urlEncode("true")
params += "meetingID" -> BreakoutRoomsUtil.urlEncode("random-1853792")
params += "password" -> BreakoutRoomsUtil.urlEncode("mp")
params += "role" -> BreakoutRoomsUtil.urlEncode("MODERATOR")
params += "redirect" -> BreakoutRoomsUtil.urlEncode("true")

val result = BreakoutRoomsUtil.createBaseString(params.toMap)
Expand All @@ -36,18 +36,18 @@ class BreakoutRoomsUtilSpec extends UnitSpec {

it should "calculate the checksum of join url" in {
val sharedSecret = "a820d30da2db356124fce5bd5d8054b4"
val checksum = "6baef866df491ae82df992eb14f7f8511d5b77f3"
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
val checksum = "3e84497a600e67476b73c4bb9cce3a3de413dcfa"
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&redirect=true&role=MODERATOR"

val joinChecksum = BreakoutRoomsUtil.calculateChecksum("join", baseString, sharedSecret)
assert(joinChecksum == checksum)
}

it should "create a join api url" in {
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&redirect=true&role=MODERATOR"
val webAPI = "http://www.example.com/bigbluebutton/api/"
val joinAPI = "join"
val checksum = "6baef866df491ae82df992eb14f7f8511d5b77f3"
val checksum = "3e84497a600e67476b73c4bb9cce3a3de413dcfa"

val joinURL = webAPI.concat(joinAPI).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
val result = BreakoutRoomsUtil.createJoinURL(webAPI, joinAPI, baseString, checksum);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ public class ApiParams {
public static final String ALLOW_MODS_TO_EJECT_CAMERAS = "allowModsToEjectCameras";
public static final String NAME = "name";
public static final String PARENT_MEETING_ID = "parentMeetingID";
public static final String PASSWORD = "password";
public static final String RECORD = "record";
public static final String RECORD_ID = "recordID";
public static final String REDIRECT = "redirect";
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.shared.JoinPassword;
import org.bigbluebutton.api.model.shared.Password;

import jakarta.validation.Valid;
import jakarta.servlet.http.HttpServletRequest;

import java.util.Collections;
Expand All @@ -19,7 +16,6 @@ public enum Params implements RequestParameters {
MEETING_ID("meetingID"),
USER_ID("userID"),
FULL_NAME("fullName"),
PASSWORD("password"),
GUEST("guest"),
AUTH("auth"),
CREATE_TIME("createTime"),
Expand All @@ -42,9 +38,6 @@ public enum Params implements RequestParameters {
@NotEmpty(key = "missingParamFullName", message = "You must provide your name")
private String fullName;

@PasswordConstraint
private String password;

@IsBooleanConstraint(message = "Guest must be a boolean value (true or false)")
private String guestString;
private Boolean guest;
Expand All @@ -59,12 +52,8 @@ public enum Params implements RequestParameters {

private String role;

@Valid
private Password joinPassword;

public JoinMeeting(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum, servletRequest);
joinPassword = new JoinPassword();
}

@Override
Expand Down Expand Up @@ -96,14 +85,6 @@ public void setFullName(String fullName) {
this.fullName = fullName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public void setGuestString(String guestString) { this.guestString = guestString; }

public Boolean getGuest() {
Expand Down Expand Up @@ -146,18 +127,11 @@ public void setRole(String role) {
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.MEETING_ID.getValue())) {
setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
joinPassword.setMeetingID(meetingID);
}

if(params.containsKey(Params.USER_ID.getValue())) setUserID(params.get(Params.USER_ID.getValue())[0]);
if(params.containsKey(Params.FULL_NAME.getValue())) setFullName(params.get(Params.FULL_NAME.getValue())[0]);

if(params.containsKey(Params.PASSWORD.getValue())) {
setPassword(params.get(Params.PASSWORD.getValue())[0]);
joinPassword.setPassword(password);
}


if(params.containsKey(Params.GUEST.getValue())) setGuestString(params.get(Params.GUEST.getValue())[0]);
if(params.containsKey(Params.AUTH.getValue())) setAuthString(params.get(Params.AUTH.getValue())[0]);
if(params.containsKey(Params.CREATE_TIME.getValue())) setCreateTimeString(params.get(Params.CREATE_TIME.getValue())[0]);
Expand Down

This file was deleted.

This file was deleted.

4 changes: 2 additions & 2 deletions bigbluebutton-tests/playwright/core/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ export async function createMeeting(
export function getJoinURL({ meetingID, fullName, options }: GetJoinUrlProp): string {
const { isModerator, joinParameter, skipSessionDetailsModal } = options || {};

const pw = isModerator ? parameters.moderatorPW : parameters.attendeePW;
const role = isModerator ? 'MODERATOR' : 'VIEWER';
const shouldSkipSessionDetailsModal = skipSessionDetailsModal
? '&userdata-bbb_show_session_details_on_join=false'
: ''; // default value in settings.yml is true
const baseQuery = `fullName=${fullName}&meetingID=${meetingID}`
+ `&password=${pw}${shouldSkipSessionDetailsModal}`; // prettier-ignore
+ `&role=${role}${shouldSkipSessionDetailsModal}`; // prettier-ignore
const query = joinParameter !== undefined ? `${baseQuery}&${joinParameter}` : baseQuery;
const apiCall = `join${query}${parameters.secret}`;
const checksum = getChecksum(apiCall, parameters.secret!);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,6 @@ class ApiController {

String fullName = ParamsUtil.stripControlChars(params.fullName)

String attPW = params.password

Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID);

// the createTime mismatch with meeting's createTime, complain
Expand All @@ -396,66 +394,25 @@ class ApiController {
}

// Now determine if this user is a moderator or a viewer.
// The user's role is set exclusively through the `role` parameter.
// The legacy password-based role selection (`password` parameter, matched
// against the meeting's moderator/attendee passwords) was deprecated in
// BigBlueButton 3.0 and removed in 4.0 — use `role` (MODERATOR or VIEWER) instead.
String role = null;

// First Case: send a valid role
if (!StringUtils.isEmpty(params.role) && roles.containsKey(params.role.toLowerCase())) {
role = roles.get(params.role.toLowerCase());

// Second case: role is not present or valid BUT there is password
} else if (attPW != null && !attPW.isEmpty()){
// Check if the meeting has passwords
if ((meeting.getModeratorPassword() != null && !meeting.getModeratorPassword().isEmpty())
&& (meeting.getViewerPassword() != null && !meeting.getViewerPassword().isEmpty())){
// Check which role does the user belong
if (meeting.getModeratorPassword().equals(attPW)) {
role = Meeting.ROLE_MODERATOR
} else if (meeting.getViewerPassword().equals(attPW)) {
role = Meeting.ROLE_ATTENDEE
} else {
log.debug("Password does not match any of the registered ones");
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
render(text: responseBuilder.buildError("Params required", "You must enter a valid password",
RESP_CODE_FAILED), contentType: "text/xml")
}
'*' {
render(text: responseBuilder.buildError("Params required", "You must enter a valid password",
RESP_CODE_FAILED), contentType: "text/xml")
}
}
return
}
// In this case, the meeting doesn't have any password registered and there is no role param
} else {
log.debug("This meeting doesn't have any password");
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
render(text: responseBuilder.buildError("Params required", "You must send the 'role' parameter, since " +
"this meeting doesn't have any password.", RESP_CODE_FAILED), contentType: "text/xml")
}
'*' {
render(text: responseBuilder.buildError("Params required", "You must send the 'role' parameter, since " +
"this meeting doesn't have any password.", RESP_CODE_FAILED), contentType: "text/xml")
}
}
return
}

// Third case: No valid role + no valid password
} else {
log.debug("No matching params encountered");
log.debug("No valid role provided");
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
render(text: responseBuilder.buildError("Params required", "You must either send the valid role of the user, or " +
"the password, sould the meeting has one.", RESP_CODE_FAILED), contentType: "text/xml")
render(text: responseBuilder.buildError("Params required", "You must provide a valid 'role' parameter " +
"(MODERATOR or VIEWER).", RESP_CODE_FAILED), contentType: "text/xml")
}
'*' {
render(text: responseBuilder.buildError("Params required", "You must either send the valid role of the user, or " +
"the password, sould the meeting has one.", RESP_CODE_FAILED), contentType: "text/xml")
render(text: responseBuilder.buildError("Params required", "You must provide a valid 'role' parameter " +
"(MODERATOR or VIEWER).", RESP_CODE_FAILED), contentType: "text/xml")
}
}
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,9 @@ class ApiControllerSpec extends Specification implements ControllerUnitTest<ApiC

and: "a user joins the meeting"
def mmetingId = params[ApiParams.MEETING_ID]
def password = params[ApiParams.MODERATOR_PW]
resetWebCall()

createJoinUser(password)
createJoinUser()
params[ApiParams.MEETING_ID] = mmetingId
setChecksumAndQueryString('join')
controller.join()
Expand Down Expand Up @@ -267,10 +266,10 @@ class ApiControllerSpec extends Specification implements ControllerUnitTest<ApiC
params[ApiParams.VOICE_BRIDGE] = Objects.toString(faker.number().numberBetween(25000, 80000))
}

def createJoinUser(password) {
def createJoinUser() {
params[ApiParams.MEETING_ID] = faker.educator().course()
params[ApiParams.FULL_NAME] = faker.superhero().name()
params[ApiParams.PASSWORD] = password
params[ApiParams.ROLE] = "MODERATOR"
params[ApiParams.REDIRECT] = "false"
}

Expand Down
13 changes: 3 additions & 10 deletions docs/docs/data/join.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,16 @@ const joinEndpointTableData = [
"description": (<>The meeting ID that identifies the meeting you are attempting to join.</>)
},
{
"name": "password",
"name": "role",
"required": true,
"type": "String",
deprecated: true,
"description": (
<>
<p>This password value is used to determine the role of the user. It must match either the moderator or attendee password.</p>
<p><i>Deprecated:</i> Use the <code>role</code> parameter instead to directly set the user's role. If the <code>role</code> parameter is passed, then the <code>password</code> parameter is not required.</p>
<p>Defines the user's role for the meeting. Valid values are <code>MODERATOR</code> or <code>VIEWER</code> (case insensitive).</p>
<p><i>Note:</i> the legacy <code>password</code> parameter — used to derive the role by matching the meeting's moderator/attendee password — was deprecated in BigBlueButton 3.0 and <b>removed in 4.0</b>. Use <code>role</code> instead.</p>
</>
)
},
{
"name": "role",
"required": true,
"type": "String",
"description": (<>Define user role for the meeting. Valid values are MODERATOR or VIEWER (case insensitive). If the role parameter is present and valid, it overrides the password parameter. You must specify either password parameter or role parameter in the join request.</>)
},
{
"name": "createTime",
"required": false,
Expand Down
Loading
Loading