Merge pull request from GHSA-4m48-49h7-f3c4

fix(sec): API fix duplicates ghsa-4m48-49h7-f3c4 (2.7)
This commit is contained in:
Anton Georgiev 2024-05-15 10:38:58 -04:00 committed by GitHub
commit ea6e9461dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 323 additions and 231 deletions

View File

@ -112,5 +112,7 @@ libraryDependencies ++= Seq(
"com.zaxxer" % "HikariCP" % "4.0.3", "com.zaxxer" % "HikariCP" % "4.0.3",
"commons-validator" % "commons-validator" % "1.7", "commons-validator" % "commons-validator" % "1.7",
"org.apache.tika" % "tika-core" % "2.8.0", "org.apache.tika" % "tika-core" % "2.8.0",
"org.apache.tika" % "tika-parsers-standard-package" % "2.8.0" "org.apache.tika" % "tika-parsers-standard-package" % "2.8.0",
"org.scala-lang.modules" %% "scala-xml" % "2.2.0",
"jakarta.ws.rs" % "jakarta.ws.rs-api" % "3.1.0"
) )

View File

@ -1,22 +1,22 @@
package org.bigbluebutton.api.model.constraint; package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.PostChecksumValidator; import org.bigbluebutton.api.model.validator.ContentTypeValidator;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = PostChecksumValidator.class) @Constraint(validatedBy = ContentTypeValidator.class)
@Target(TYPE) @Target(TYPE)
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface PostChecksumConstraint { public @interface ContentTypeConstraint {
String key() default "checksumError"; String key() default "unsupportedContentType";
String message() default "Checksums do not match"; String message() default "POST request Content-Type is missing or unsupported";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {}; Class<? extends Payload>[] payload() default {};
} }

View File

@ -1,12 +1,16 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import jakarta.ws.rs.core.MediaType;
import org.bigbluebutton.api.model.constraint.*; import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Map; import java.util.Map;
import java.util.Set;
@ContentTypeConstraint
public class CreateMeeting extends RequestWithChecksum<CreateMeeting.Params> { public class CreateMeeting extends RequestWithChecksum<CreateMeeting.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -51,8 +55,8 @@ public class CreateMeeting extends RequestWithChecksum<CreateMeeting.Params> {
private String recordString; private String recordString;
private Boolean record; private Boolean record;
public CreateMeeting(Checksum checksum) { public CreateMeeting(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
} }
public String getName() { public String getName() {
@ -138,4 +142,9 @@ public class CreateMeeting extends RequestWithChecksum<CreateMeeting.Params> {
isBreakoutRoom = Boolean.parseBoolean(isBreakoutRoomString); isBreakoutRoom = Boolean.parseBoolean(isBreakoutRoomString);
record = Boolean.parseBoolean(recordString); record = Boolean.parseBoolean(recordString);
} }
@Override
public Set<String> getSupportedContentTypes() {
return Set.of(MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_XML, MediaType.TEXT_XML);
}
} }

View File

@ -1,16 +1,16 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint; import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.constraint.NotEmpty;
import org.bigbluebutton.api.model.constraint.PasswordConstraint;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.shared.ModeratorPassword; import org.bigbluebutton.api.model.shared.ModeratorPassword;
import org.bigbluebutton.api.model.shared.Password; import org.bigbluebutton.api.model.shared.Password;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Map; import java.util.Map;
import java.util.Set;
@ContentTypeConstraint
public class EndMeeting extends RequestWithChecksum<EndMeeting.Params> { public class EndMeeting extends RequestWithChecksum<EndMeeting.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -34,8 +34,8 @@ public class EndMeeting extends RequestWithChecksum<EndMeeting.Params> {
@Valid @Valid
private Password moderatorPassword; private Password moderatorPassword;
public EndMeeting(Checksum checksum) { public EndMeeting(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
moderatorPassword = new ModeratorPassword(); moderatorPassword = new ModeratorPassword();
} }

View File

@ -1,11 +1,16 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import jakarta.ws.rs.core.MediaType;
import org.bigbluebutton.api.model.constraint.*; import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.service.SessionService; import org.bigbluebutton.api.service.SessionService;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Map; import java.util.Map;
import java.util.Set;
public class Enter implements Request<Enter.Params> { @ContentTypeConstraint
public class Enter extends RequestWithSession<Enter.Params>{
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken"); SESSION_TOKEN("sessionToken");
@ -27,7 +32,8 @@ public class Enter implements Request<Enter.Params> {
private SessionService sessionService; private SessionService sessionService;
public Enter() { public Enter(HttpServletRequest servletRequest) {
super(servletRequest);
sessionService = new SessionService(); sessionService = new SessionService();
} }

View File

@ -2,9 +2,10 @@ package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint; import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
public class GetJoinUrl implements Request<GetJoinUrl.Params> { public class GetJoinUrl extends RequestWithSession<GetJoinUrl.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken"); SESSION_TOKEN("sessionToken");
@ -19,6 +20,10 @@ public class GetJoinUrl implements Request<GetJoinUrl.Params> {
@UserSessionConstraint @UserSessionConstraint
private String sessionToken; private String sessionToken;
public GetJoinUrl(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String getSessionToken() { public String getSessionToken() {
return sessionToken; return sessionToken;
} }

View File

@ -1,14 +1,17 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.ContentTypeConstraint;
import org.bigbluebutton.api.model.constraint.MeetingEndedConstraint; import org.bigbluebutton.api.model.constraint.MeetingEndedConstraint;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint; import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint; import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import org.bigbluebutton.api.service.SessionService; import org.bigbluebutton.api.service.SessionService;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Map; import java.util.Map;
public class GuestWait implements Request<GuestWait.Params> { @ContentTypeConstraint
public class GuestWait extends RequestWithSession<GuestWait.Params>{
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken"); SESSION_TOKEN("sessionToken");
@ -29,7 +32,8 @@ public class GuestWait implements Request<GuestWait.Params> {
private SessionService sessionService; private SessionService sessionService;
public GuestWait() { public GuestWait(HttpServletRequest servletRequest) {
super(servletRequest);
sessionService = new SessionService(); sessionService = new SessionService();
} }

View File

@ -1,11 +1,14 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import jakarta.ws.rs.core.MediaType;
import org.bigbluebutton.api.model.constraint.*; import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
import java.util.Set;
@ContentTypeConstraint
public class InsertDocument extends RequestWithChecksum<InsertDocument.Params> { public class InsertDocument extends RequestWithChecksum<InsertDocument.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -21,8 +24,8 @@ public class InsertDocument extends RequestWithChecksum<InsertDocument.Params> {
@MeetingIDConstraint @MeetingIDConstraint
private String meetingID; private String meetingID;
public InsertDocument(Checksum checksum) { public InsertDocument(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
} }
public String getMeetingID() { public String getMeetingID() {
@ -37,4 +40,9 @@ public class InsertDocument extends RequestWithChecksum<InsertDocument.Params> {
public void populateFromParamsMap(Map<String, String[]> params) { public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.MEETING_ID.getValue())) setMeetingID(params.get(Params.MEETING_ID.getValue())[0]); if(params.containsKey(Params.MEETING_ID.getValue())) setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
} }
@Override
public Set<String> getSupportedContentTypes() {
return Set.of(MediaType.APPLICATION_XML, MediaType.TEXT_XML);
}
} }

View File

@ -5,9 +5,11 @@ import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.shared.JoinPassword; import org.bigbluebutton.api.model.shared.JoinPassword;
import org.bigbluebutton.api.model.shared.Password; import org.bigbluebutton.api.model.shared.Password;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Map; import java.util.Map;
@ContentTypeConstraint
public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> { public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -57,8 +59,8 @@ public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
@Valid @Valid
private Password joinPassword; private Password joinPassword;
public JoinMeeting(Checksum checksum) { public JoinMeeting(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
joinPassword = new JoinPassword(); joinPassword = new JoinPassword();
} }

View File

@ -2,10 +2,11 @@ package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint; import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Map; import java.util.Map;
public class LearningDashboard implements Request<LearningDashboard.Params> { public class LearningDashboard extends RequestWithSession<LearningDashboard.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken"); SESSION_TOKEN("sessionToken");
@ -20,6 +21,10 @@ public class LearningDashboard implements Request<LearningDashboard.Params> {
@UserSessionConstraint @UserSessionConstraint
private String sessionToken; private String sessionToken;
public LearningDashboard(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String getSessionToken() { public String getSessionToken() {
return sessionToken; return sessionToken;
} }

View File

@ -1,11 +1,14 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.ContentTypeConstraint;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint; import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint; import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
@ContentTypeConstraint
public class MeetingInfo extends RequestWithChecksum<MeetingInfo.Params> { public class MeetingInfo extends RequestWithChecksum<MeetingInfo.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -22,8 +25,8 @@ public class MeetingInfo extends RequestWithChecksum<MeetingInfo.Params> {
@MeetingExistsConstraint @MeetingExistsConstraint
private String meetingID; private String meetingID;
public MeetingInfo(Checksum checksum) { public MeetingInfo(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
} }
public String getMeetingID() { public String getMeetingID() {

View File

@ -1,10 +1,13 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.ContentTypeConstraint;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint; import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
@ContentTypeConstraint
public class MeetingRunning extends RequestWithChecksum<MeetingRunning.Params> { public class MeetingRunning extends RequestWithChecksum<MeetingRunning.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -20,8 +23,8 @@ public class MeetingRunning extends RequestWithChecksum<MeetingRunning.Params> {
@MeetingIDConstraint @MeetingIDConstraint
private String meetingID; private String meetingID;
public MeetingRunning(Checksum checksum) { public MeetingRunning(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
} }
public String getMeetingID() { public String getMeetingID() {

View File

@ -1,9 +1,13 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
import java.util.Set;
public interface Request<P extends Enum<P> & RequestParameters> { public interface Request<P extends Enum<P> & RequestParameters> {
void populateFromParamsMap(Map<String, String[]> params); void populateFromParamsMap(Map<String, String[]> params);
void convertParamsFromString(); void convertParamsFromString();
Set<String> getSupportedContentTypes();
HttpServletRequest getServletRequest();
} }

View File

@ -1,17 +1,23 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import jakarta.ws.rs.core.MediaType;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Map; import java.util.Map;
import java.util.Set;
public abstract class RequestWithChecksum<P extends Enum<P> & RequestParameters> implements Request<P> { public abstract class RequestWithChecksum<P extends Enum<P> & RequestParameters> implements Request<P> {
@Valid @Valid
protected Checksum checksum; protected Checksum checksum;
protected RequestWithChecksum(Checksum checksum) { protected HttpServletRequest servletRequest;
protected RequestWithChecksum(Checksum checksum, HttpServletRequest servletRequest) {
this.checksum = checksum; this.checksum = checksum;
this.servletRequest = servletRequest;
} }
public Checksum getChecksum() { public Checksum getChecksum() {
@ -27,4 +33,14 @@ public abstract class RequestWithChecksum<P extends Enum<P> & RequestParameters>
public void convertParamsFromString() { public void convertParamsFromString() {
} }
@Override
public Set<String> getSupportedContentTypes() {
return Set.of(MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA);
}
@Override
public HttpServletRequest getServletRequest() {
return servletRequest;
}
} }

View File

@ -0,0 +1,25 @@
package org.bigbluebutton.api.model.request;
import jakarta.ws.rs.core.MediaType;
import javax.servlet.http.HttpServletRequest;
import java.util.Set;
public abstract class RequestWithSession<P extends Enum<P> & RequestParameters> implements Request<P> {
protected HttpServletRequest servletRequest;
protected RequestWithSession(HttpServletRequest servletRequest) {
this.servletRequest = servletRequest;
}
@Override
public Set<String> getSupportedContentTypes() {
return Set.of(MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA);
}
@Override
public HttpServletRequest getServletRequest() {
return servletRequest;
}
}

View File

@ -2,10 +2,11 @@ package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint; import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Map; import java.util.Map;
public class SignOut implements Request<SignOut.Params> { public class SignOut extends RequestWithSession<SignOut.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken"); SESSION_TOKEN("sessionToken");
@ -20,6 +21,10 @@ public class SignOut implements Request<SignOut.Params> {
@UserSessionConstraint @UserSessionConstraint
private String sessionToken; private String sessionToken;
public SignOut(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String getSessionToken() { public String getSessionToken() {
return sessionToken; return sessionToken;
} }

View File

@ -1,9 +1,12 @@
package org.bigbluebutton.api.model.request; package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.ContentTypeConstraint;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
@ContentTypeConstraint
public class SimpleRequest extends RequestWithChecksum<SimpleRequest.Params> { public class SimpleRequest extends RequestWithChecksum<SimpleRequest.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
@ -16,8 +19,8 @@ public class SimpleRequest extends RequestWithChecksum<SimpleRequest.Params> {
public String getValue() { return value; } public String getValue() { return value; }
} }
public SimpleRequest(Checksum checksum) { public SimpleRequest(Checksum checksum, HttpServletRequest servletRequest) {
super(checksum); super(checksum, servletRequest);
} }
@Override @Override

View File

@ -3,9 +3,11 @@ package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.*; import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.service.SessionService; import org.bigbluebutton.api.service.SessionService;
import javax.servlet.http.HttpServletRequest;
import java.util.Map; import java.util.Map;
public class Stuns implements Request<Stuns.Params> { @ContentTypeConstraint
public class Stuns extends RequestWithSession<Stuns.Params> {
public enum Params implements RequestParameters { public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken"); SESSION_TOKEN("sessionToken");
@ -26,7 +28,10 @@ public class Stuns implements Request<Stuns.Params> {
private SessionService sessionService; private SessionService sessionService;
public Stuns() { sessionService = new SessionService(); } public Stuns(HttpServletRequest servletRequest) {
super(servletRequest);
sessionService = new SessionService();
}
public String getSessionToken() { public String getSessionToken() {
return sessionToken; return sessionToken;

View File

@ -1,8 +1,11 @@
package org.bigbluebutton.api.model.shared; package org.bigbluebutton.api.model.shared;
import org.bigbluebutton.api.model.constraint.ContentTypeConstraint;
import org.bigbluebutton.api.model.constraint.NotEmpty; import org.bigbluebutton.api.model.constraint.NotEmpty;
import org.bigbluebutton.api.util.ParamsUtil; import org.bigbluebutton.api.util.ParamsUtil;
import javax.servlet.http.HttpServletRequest;
public abstract class Checksum { public abstract class Checksum {
@NotEmpty(message = "You must provide the API call", groups = ChecksumValidationGroup.class) @NotEmpty(message = "You must provide the API call", groups = ChecksumValidationGroup.class)
@ -13,9 +16,12 @@ public abstract class Checksum {
protected String queryStringWithoutChecksum; protected String queryStringWithoutChecksum;
public Checksum(String apiCall, String checksum) { protected HttpServletRequest request;
public Checksum(String apiCall, String checksum, HttpServletRequest request) {
this.apiCall = ParamsUtil.sanitizeString(apiCall); this.apiCall = ParamsUtil.sanitizeString(apiCall);
this.checksum = ParamsUtil.sanitizeString(checksum); this.checksum = ParamsUtil.sanitizeString(checksum);
this.request = request;
} }
public String getApiCall() { public String getApiCall() {
@ -41,4 +47,12 @@ public abstract class Checksum {
public void setQueryStringWithoutChecksum(String queryStringWithoutChecksum) { public void setQueryStringWithoutChecksum(String queryStringWithoutChecksum) {
this.queryStringWithoutChecksum = queryStringWithoutChecksum; this.queryStringWithoutChecksum = queryStringWithoutChecksum;
} }
public void setRequest(HttpServletRequest request) {
this.request = request;
}
public HttpServletRequest getRequest() {
return request;
}
} }

View File

@ -3,16 +3,16 @@ package org.bigbluebutton.api.model.shared;
import org.bigbluebutton.api.model.constraint.GetChecksumConstraint; import org.bigbluebutton.api.model.constraint.GetChecksumConstraint;
import org.bigbluebutton.api.util.ParamsUtil; import org.bigbluebutton.api.util.ParamsUtil;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
@GetChecksumConstraint(groups = ChecksumValidationGroup.class) @GetChecksumConstraint(groups = ChecksumValidationGroup.class)
public class GetChecksum extends Checksum { public class GetChecksum extends Checksum {
@NotEmpty(message = "You must provide the query string")
private String queryString; private String queryString;
public GetChecksum(String apiCall, String checksum, String queryString) { public GetChecksum(String apiCall, String checksum, String queryString, HttpServletRequest request) {
super(apiCall, checksum); super(apiCall, checksum, request);
this.queryString = ParamsUtil.sanitizeString(queryString); this.queryString = ParamsUtil.sanitizeString(queryString);
removeChecksumFromQueryString(); removeChecksumFromQueryString();
} }

View File

@ -1,22 +0,0 @@
package org.bigbluebutton.api.model.shared;
import org.bigbluebutton.api.model.constraint.PostChecksumConstraint;
import org.bigbluebutton.api.service.ValidationService;
import java.util.Map;
@PostChecksumConstraint(groups = ChecksumValidationGroup.class)
public class PostChecksum extends Checksum {
Map<String, String[]> params;
public PostChecksum(String apiCall, String checksum, Map<String, String[]> params) {
super(apiCall, checksum);
this.params = params;
queryStringWithoutChecksum = ValidationService.buildQueryStringFromParamsMap(params);
}
public Map<String, String[]> getParams() { return params; }
public void setParams(Map<String, String[]> params) { this.params = params; }
}

View File

@ -0,0 +1,40 @@
package org.bigbluebutton.api.model.validator;
import jakarta.ws.rs.core.MediaType;
import org.apache.commons.compress.utils.Sets;
import org.bigbluebutton.api.model.constraint.ContentTypeConstraint;
import org.bigbluebutton.api.model.request.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Set;
public class ContentTypeValidator implements ConstraintValidator<ContentTypeConstraint, Request> {
private static final Logger log = LoggerFactory.getLogger(ContentTypeValidator.class);
@Override
public void initialize(ContentTypeConstraint constraintAnnotation) {}
@Override
public boolean isValid(Request request, ConstraintValidatorContext context) {
HttpServletRequest servletRequest = request.getServletRequest();
String requestMethod = servletRequest.getMethod();
String contentType = servletRequest.getContentType();
String contentTypeHeader = servletRequest.getHeader("Content-Type");
log.info("Validating {} request with content type {}", requestMethod, contentType);
boolean requestBodyPresent = servletRequest.getContentLength() > 0;
if (requestBodyPresent) {
if (contentType == null || contentTypeHeader == null) return false;
else {
return request.getSupportedContentTypes().contains(contentType);
}
}
return true;
}
}

View File

@ -1,8 +1,10 @@
package org.bigbluebutton.api.model.validator; package org.bigbluebutton.api.model.validator;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintValidatorContext;
import jakarta.ws.rs.core.MediaType;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.bigbluebutton.api.model.constraint.GetChecksumConstraint; import org.bigbluebutton.api.model.constraint.GetChecksumConstraint;
import org.bigbluebutton.api.model.shared.GetChecksum; import org.bigbluebutton.api.model.shared.GetChecksum;

View File

@ -1,53 +0,0 @@
package org.bigbluebutton.api.model.validator;
import org.apache.commons.codec.digest.DigestUtils;
import org.bigbluebutton.api.model.constraint.PostChecksumConstraint;
import org.bigbluebutton.api.model.shared.PostChecksum;
import org.bigbluebutton.api.service.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PostChecksumValidator implements ConstraintValidator<PostChecksumConstraint, PostChecksum> {
private static Logger log = LoggerFactory.getLogger(PostChecksumValidator.class);
@Override
public void initialize(PostChecksumConstraint constraintAnnotation) {}
@Override
public boolean isValid(PostChecksum checksum, ConstraintValidatorContext context) {
String securitySalt = ServiceUtils.getValidationService().getSecuritySalt();
if (securitySalt.isEmpty()) {
log.warn("Security is disabled in this service. Make sure this is intentional.");
return true;
}
String queryStringWithoutChecksum = checksum.getQueryStringWithoutChecksum();
log.info("query string after checksum removed: [{}]", queryStringWithoutChecksum);
if(queryStringWithoutChecksum == null) {
return false;
}
String providedChecksum = checksum.getChecksum();
log.info("CHECKSUM={} length={}", providedChecksum, providedChecksum.length());
if(providedChecksum == null) {
return false;
}
String data = checksum.getApiCall() + queryStringWithoutChecksum + securitySalt;
String createdCheckSum = DigestUtils.sha1Hex(data);
if (createdCheckSum == null || !createdCheckSum.equalsIgnoreCase(providedChecksum)) {
log.info("checksumError: failed checksum. our checksum: [{}], client: [{}]", createdCheckSum, providedChecksum);
return false;
}
return true;
}
}

View File

@ -4,11 +4,11 @@ import org.bigbluebutton.api.model.request.*;
import org.bigbluebutton.api.model.shared.Checksum; import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.shared.ChecksumValidationGroup; import org.bigbluebutton.api.model.shared.ChecksumValidationGroup;
import org.bigbluebutton.api.model.shared.GetChecksum; import org.bigbluebutton.api.model.shared.GetChecksum;
import org.bigbluebutton.api.model.shared.PostChecksum;
import org.bigbluebutton.api.util.ParamsUtil; import org.bigbluebutton.api.util.ParamsUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
import javax.validation.Validation; import javax.validation.Validation;
import javax.validation.Validator; import javax.validation.Validator;
@ -70,11 +70,13 @@ public class ValidationService {
validator = validatorFactory.getValidator(); validator = validatorFactory.getValidator();
} }
public Map<String, String> validate(ApiCall apiCall, Map<String, String[]> params, String queryString) { public Map<String, String> validate(ApiCall apiCall, HttpServletRequest servletRequest) {
String queryString = servletRequest.getQueryString();
Map<String, String[]> params = servletRequest.getParameterMap();
log.info("Validating {} request with query string {}", apiCall.getName(), queryString); log.info("Validating {} request with query string {}", apiCall.getName(), queryString);
params = sanitizeParams(params); params = sanitizeParams(params);
Request request = initializeRequest(apiCall, params, queryString); Request request = initializeRequest(apiCall, params, queryString, servletRequest);
Map<String,String> violations = new HashMap<>(); Map<String,String> violations = new HashMap<>();
if(request == null) { if(request == null) {
@ -101,7 +103,7 @@ public class ValidationService {
} }
} }
private Request initializeRequest(ApiCall apiCall, Map<String, String[]> params, String queryString) { private Request initializeRequest(ApiCall apiCall, Map<String, String[]> params, String queryString, HttpServletRequest servletRequest) {
Request request = null; Request request = null;
Checksum checksum; Checksum checksum;
@ -110,55 +112,23 @@ public class ValidationService {
checksumValue = params.get("checksum")[0]; checksumValue = params.get("checksum")[0];
} }
if(queryString == null || queryString.isEmpty()) { if (Objects.requireNonNull(apiCall.requestType) == RequestType.GET) {
queryString = buildQueryStringFromParamsMap(params); checksum = new GetChecksum(apiCall.getName(), checksumValue, queryString, servletRequest);
} request = switch (apiCall) {
case CREATE -> new CreateMeeting(checksum, servletRequest);
switch(apiCall.requestType) { case JOIN -> new JoinMeeting(checksum, servletRequest);
case GET: case MEETING_RUNNING -> new MeetingRunning(checksum, servletRequest);
checksum = new GetChecksum(apiCall.getName(), checksumValue, queryString); case END -> new EndMeeting(checksum, servletRequest);
switch(apiCall) { case GET_MEETING_INFO -> new MeetingInfo(checksum, servletRequest);
case CREATE: case GET_MEETINGS, GET_SESSIONS -> new SimpleRequest(checksum, servletRequest);
request = new CreateMeeting(checksum); case INSERT_DOCUMENT -> new InsertDocument(checksum, servletRequest);
break; case GUEST_WAIT -> new GuestWait(servletRequest);
case JOIN: case ENTER -> new Enter(servletRequest);
request = new JoinMeeting(checksum); case STUNS -> new Stuns(servletRequest);
break; case SIGN_OUT -> new SignOut(servletRequest);
case MEETING_RUNNING: case LEARNING_DASHBOARD -> new LearningDashboard(servletRequest);
request = new MeetingRunning(checksum); case GET_JOIN_URL -> new GetJoinUrl(servletRequest);
break; };
case END:
request = new EndMeeting(checksum);
break;
case GET_MEETING_INFO:
request = new MeetingInfo(checksum);
break;
case GET_MEETINGS:
case GET_SESSIONS:
request = new SimpleRequest(checksum);
break;
case INSERT_DOCUMENT:
request = new InsertDocument(checksum);
break;
case GUEST_WAIT:
request = new GuestWait();
break;
case ENTER:
request = new Enter();
break;
case STUNS:
request = new Stuns();
break;
case SIGN_OUT:
request = new SignOut();
break;
case LEARNING_DASHBOARD:
request = new LearningDashboard();
break;
case GET_JOIN_URL:
request = new GetJoinUrl();
break;
}
} }
return request; return request;

View File

@ -63,6 +63,26 @@ class UrlMappings {
action = [GET: 'downloadFile'] action = [GET: 'downloadFile']
} }
"/bigbluebutton/api/create"(controller: "api") {
action = [GET: 'create', POST: 'create']
}
"/bigbluebutton/api/join"(controller: "api") {
action = [GET: 'join']
}
"/bigbluebutton/api/isMeetingRunning"(controller: "api") {
action = [GET: 'isMeetingRunning', POST: 'isMeetingRunning']
}
"/bigbluebutton/api/end"(controller: "api") {
action = [GET: 'end', POST: 'end']
}
"/bigbluebutton/api/getMeetingInfo"(controller: "api") {
action = [GET: 'getMeetingInfo', POST: 'getMeetingInfo']
}
"/bigbluebutton/api/getMeetings"(controller: "api") { "/bigbluebutton/api/getMeetings"(controller: "api") {
action = [GET: 'getMeetingsHandler', POST: 'getMeetingsHandler'] action = [GET: 'getMeetingsHandler', POST: 'getMeetingsHandler']
} }
@ -71,6 +91,30 @@ class UrlMappings {
action = [GET: 'getSessionsHandler', POST: 'getSessionsHandler'] action = [GET: 'getSessionsHandler', POST: 'getSessionsHandler']
} }
"/bigbluebutton/api/enter"(controller: "api") {
action = [GET: 'enter', POST: 'enter']
}
"/bigbluebutton/api/stuns"(controller: "api") {
action = [GET: 'stuns', POST: 'stuns']
}
"/bigbluebutton/api/signOut"(controller: "api") {
action = [GET: 'signOut', POST: 'signOut']
}
"/bigbluebutton/api/insertDocument"(controller: "api") {
action = [GET: 'insertDocument', POST: 'insertDocument']
}
"/bigbluebutton/api/getJoinUrl"(controller: "api") {
action = [GET: 'getJoinUrl', POST: 'getJoinUrl']
}
"/bigbluebutton/api/learningDashboard"(controller: "api") {
action = [GET: 'learningDashboard', POST: 'learningDashboard']
}
"/bigbluebutton/api/getRecordings"(controller: "recording") { "/bigbluebutton/api/getRecordings"(controller: "recording") {
action = [GET: 'getRecordingsHandler', POST: 'getRecordingsHandler'] action = [GET: 'getRecordingsHandler', POST: 'getRecordingsHandler']
} }

View File

@ -45,6 +45,7 @@ import org.bigbluebutton.web.services.turn.StunServer
import org.bigbluebutton.web.services.turn.RemoteIceCandidate import org.bigbluebutton.web.services.turn.RemoteIceCandidate
import org.json.JSONArray import org.json.JSONArray
import javax.servlet.ServletRequest import javax.servlet.ServletRequest
import javax.servlet.http.HttpServletRequest
class ApiController { class ApiController {
private static final String CONTROLLER_NAME = 'ApiController' private static final String CONTROLLER_NAME = 'ApiController'
@ -115,8 +116,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.CREATE, ValidationService.ApiCall.CREATE,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -208,7 +208,6 @@ class ApiController {
} }
} }
/********************************************** /**********************************************
* JOIN API * JOIN API
*********************************************/ *********************************************/
@ -220,8 +219,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.JOIN, ValidationService.ApiCall.JOIN,
request.getParameterMap(), request
request.getQueryString()
) )
HashMap<String, String> roles = new HashMap<String, String>(); HashMap<String, String> roles = new HashMap<String, String>();
@ -520,8 +518,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.MEETING_RUNNING, ValidationService.ApiCall.MEETING_RUNNING,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -551,8 +548,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.END, ValidationService.ApiCall.END,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -595,8 +591,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.GET_MEETING_INFO, ValidationService.ApiCall.GET_MEETING_INFO,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -604,7 +599,8 @@ class ApiController {
return return
} }
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(params.meetingID); String meetingId = params.list("meetingID")[0]
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(meetingId);
withFormat { withFormat {
xml { xml {
@ -622,8 +618,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.GET_MEETINGS, ValidationService.ApiCall.GET_MEETINGS,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -660,8 +655,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.GET_SESSIONS, ValidationService.ApiCall.GET_SESSIONS,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -719,8 +713,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.GUEST_WAIT, ValidationService.ApiCall.GUEST_WAIT,
request.getParameterMap(), request
request.getQueryString()
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
msgKey = validationResponse.getKey() msgKey = validationResponse.getKey()
@ -833,8 +826,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.ENTER, ValidationService.ApiCall.ENTER,
request.getParameterMap(), request
request.getQueryString(),
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
respMessage = validationResponse.getValue() respMessage = validationResponse.getValue()
@ -991,8 +983,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.STUNS, ValidationService.ApiCall.STUNS,
request.getParameterMap(), request
request.getQueryString(),
) )
if(!(validationResponse == null)) { if(!(validationResponse == null)) {
@ -1068,8 +1059,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.SIGN_OUT, ValidationService.ApiCall.SIGN_OUT,
request.getParameterMap(), request
request.getQueryString()
) )
if(validationResponse == null) { if(validationResponse == null) {
@ -1113,8 +1103,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.INSERT_DOCUMENT, ValidationService.ApiCall.INSERT_DOCUMENT,
request.getParameterMap(), request
request.getQueryString()
) )
def externalMeetingId = params.meetingID.toString() def externalMeetingId = params.meetingID.toString()
@ -1166,8 +1155,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.GET_JOIN_URL, ValidationService.ApiCall.GET_JOIN_URL,
request.getParameterMap(), request
request.getQueryString(),
) )
//Validate Session //Validate Session
@ -1266,8 +1254,7 @@ class ApiController {
Map.Entry<String, String> validationResponse = validateRequest( Map.Entry<String, String> validationResponse = validateRequest(
ValidationService.ApiCall.LEARNING_DASHBOARD, ValidationService.ApiCall.LEARNING_DASHBOARD,
request.getParameterMap(), request
request.getQueryString(),
) )
//Validate Session //Validate Session
@ -1945,8 +1932,8 @@ class ApiController {
redirect(url: newUri) redirect(url: newUri)
} }
private Map.Entry<String, String> validateRequest(ValidationService.ApiCall apiCall, Map<String, String[]> params, String queryString) { private Map.Entry<String, String> validateRequest(ValidationService.ApiCall apiCall, HttpServletRequest request) {
Map<String, String> violations = validationService.validate(apiCall, params, queryString) Map<String, String> violations = validationService.validate(apiCall, request)
Map.Entry<String, String> response = null Map.Entry<String, String> response = null
if(!violations.isEmpty()) { if(!violations.isEmpty()) {

View File

@ -106,7 +106,7 @@ Updated in 2.7:
- **create** - **Added:** `preUploadedPresentation`, `preUploadedPresentationName`, `disabledFeatures` options`cameraAsContent`, `snapshotOfCurrentSlide`, `downloadPresentationOriginalFile`, `downloadPresentationConvertedToPdf`, `timer`, `learningDashboardDownloadSessionData` (2.7.5). - **create** - **Added:** `preUploadedPresentation`, `preUploadedPresentationName`, `disabledFeatures` options`cameraAsContent`, `snapshotOfCurrentSlide`, `downloadPresentationOriginalFile`, `downloadPresentationConvertedToPdf`, `timer`, `learningDashboardDownloadSessionData` (2.7.5).
- **join** - **Added:** `redirectErrorUrl`, `userdata-bbb_fullaudio_bridge` - **join** - **Added:** `redirectErrorUrl`, `userdata-bbb_fullaudio_bridge` and removed support for all HTTP request methods except GET
## API Data Types ## API Data Types
@ -211,6 +211,8 @@ To use the security model, you must be able to create a SHA-1 checksum out of th
You **MUST** send this checksum with **EVERY** API call. Since end users do not know your shared secret, they can not fake calls to the server, and they can not modify any API calls since changing a single parameter name or value by only one character will completely change the checksum required to validate the call. You **MUST** send this checksum with **EVERY** API call. Since end users do not know your shared secret, they can not fake calls to the server, and they can not modify any API calls since changing a single parameter name or value by only one character will completely change the checksum required to validate the call.
**NOTE** Checksums for POST requests must be calculated using the URL query string as well. For example, if all request parameters are in the request body then the checksum will be calculated using an empty query string.
Implementations of the SHA-1 functionality exist in nearly all programming languages. Here are example methods or links to example implementations for various languages: Implementations of the SHA-1 functionality exist in nearly all programming languages. Here are example methods or links to example implementations for various languages:
- [JavaScript](http://pajhome.org.uk/crypt/md5/) - [JavaScript](http://pajhome.org.uk/crypt/md5/)
@ -280,7 +282,7 @@ The following response parameters are standard to every call and may be returned
| message | Sometimes | String | A message that gives additional information about the status of the call. A message parameter will always be returned if the returncode was `FAILED`. A message may also be returned in some cases where returncode was `SUCCESS` if additional information would be helpful.| | message | Sometimes | String | A message that gives additional information about the status of the call. A message parameter will always be returned if the returncode was `FAILED`. A message may also be returned in some cases where returncode was `SUCCESS` if additional information would be helpful.|
| messageKey | Sometimes | String | Provides similar functionality to the message and follows the same rules. However, a message key will be much shorter and will generally remain the same for the life of the API whereas a message may change over time. If your third party application would like to internationalize or otherwise change the standard messages returned, you can look up your own custom messages based on this messageKey.| | messageKey | Sometimes | String | Provides similar functionality to the message and follows the same rules. However, a message key will be much shorter and will generally remain the same for the life of the API whereas a message may change over time. If your third party application would like to internationalize or otherwise change the standard messages returned, you can look up your own custom messages based on this messageKey.|
### create ### `GET` `POST` create
Creates a BigBlueButton meeting. Creates a BigBlueButton meeting.
@ -466,7 +468,7 @@ The receiving endpoint should respond with one of the following HTTP codes to in
All other HTTP response codes will be treated as transient errors. All other HTTP response codes will be treated as transient errors.
### join ### `GET` join
Joins a user to the meeting specified in the meetingID parameter. Joins a user to the meeting specified in the meetingID parameter.
@ -503,7 +505,7 @@ There is a XML response for this call only when the `redirect` parameter is set
</response> </response>
``` ```
### insertDocument ### `POST` insertDocument
This endpoint insert one or more documents into a running meeting via API call This endpoint insert one or more documents into a running meeting via API call
@ -555,7 +557,7 @@ curl -s -X POST "https://{your-host}/bigbluebutton/api/insertDocument?meetingID=
There is also the possibility of passing the removable and downloadable variables inside the payload, they go in the `document` tag as already demonstrated. The way it works is exactly the same as in the [(POST) create endpoint](#pre-upload-slides) There is also the possibility of passing the removable and downloadable variables inside the payload, they go in the `document` tag as already demonstrated. The way it works is exactly the same as in the [(POST) create endpoint](#pre-upload-slides)
### isMeetingRunning ### `GET` `POST` isMeetingRunning
This call enables you to simply check on whether or not a meeting is running by looking it up with your meeting ID. This call enables you to simply check on whether or not a meeting is running by looking it up with your meeting ID.
@ -584,7 +586,7 @@ http&#58;//yourserver.com/bigbluebutton/api/isMeetingRunning?meetingID=test01&ch
running can be “true” or “false” that signals whether a meeting with this ID is currently running. running can be “true” or “false” that signals whether a meeting with this ID is currently running.
### end ### `GET` `POST` end
Use this to forcibly end a meeting and kick all participants out of the meeting. Use this to forcibly end a meeting and kick all participants out of the meeting.
@ -628,7 +630,7 @@ curl --request POST \
**IMPORTANT NOTE:** You should note that when you call end meeting, it is simply sending a request to the backend (Red5) server that is handling all the conference traffic. That backend server will immediately attempt to send every connected client a logout event, kicking them from the meeting. It will then disconnect them, and the meeting will be ended. However, this may take several seconds, depending on network conditions. Therefore, the end meeting call will return a success as soon as the request is sent. But to be sure that it completed, you should then check back a few seconds later by using the `getMeetingInfo` or `isMeetingRunning` calls to verify that all participants have left the meeting and that it successfully ended. **IMPORTANT NOTE:** You should note that when you call end meeting, it is simply sending a request to the backend (Red5) server that is handling all the conference traffic. That backend server will immediately attempt to send every connected client a logout event, kicking them from the meeting. It will then disconnect them, and the meeting will be ended. However, this may take several seconds, depending on network conditions. Therefore, the end meeting call will return a success as soon as the request is sent. But to be sure that it completed, you should then check back a few seconds later by using the `getMeetingInfo` or `isMeetingRunning` calls to verify that all participants have left the meeting and that it successfully ended.
### getMeetingInfo ### `GET` `POST` getMeetingInfo
This call will return all of a meeting's information, including the list of attendees as well as start and end times. This call will return all of a meeting's information, including the list of attendees as well as start and end times.
@ -728,7 +730,7 @@ If a meeting is a breakout room itself, then `getMeetingInfo` will also return a
</response> </response>
``` ```
### getMeetings ### `GET` `POST` getMeetings
This call will return a list of all the meetings found on this server. This call will return a list of all the meetings found on this server.
@ -781,7 +783,7 @@ http&#58;//yourserver.com/bigbluebutton/api/getMeetings?checksum=1234
</response> </response>
``` ```
### getRecordings ### `GET` getRecordings
Retrieves the recordings that are available for playback for a given meetingID (or set of meeting IDs). Support for pagination was added in 2.6. Retrieves the recordings that are available for playback for a given meetingID (or set of meeting IDs). Support for pagination was added in 2.6.
@ -896,7 +898,7 @@ Here the `getRecordings` API call returned back two recordings for the meetingID
</response> </response>
``` ```
### publishRecordings ### `GET` publishRecordings
Publish and unpublish recordings for a given recordID (or set of record IDs). Publish and unpublish recordings for a given recordID (or set of record IDs).
@ -924,7 +926,7 @@ Publish and unpublish recordings for a given recordID (or set of record IDs).
</response> </response>
``` ```
### deleteRecordings ### `GET` deleteRecordings
Delete one or more recordings for a given recordID (or set of record IDs). Delete one or more recordings for a given recordID (or set of record IDs).
@ -952,7 +954,7 @@ http&#58;//yourserver.com/bigbluebutton/api/deleteRecordings?[parameters]&checks
</response> </response>
``` ```
### updateRecordings ### `GET` `POST` updateRecordings
Update metadata for a given recordID (or set of record IDs). Available since version 1.1 Update metadata for a given recordID (or set of record IDs). Available since version 1.1
@ -979,7 +981,7 @@ Update metadata for a given recordID (or set of record IDs). Available since ver
</response> </response>
``` ```
### getRecordingTextTracks ### `GET` `POST` getRecordingTextTracks
Get a list of the caption/subtitle files currently available for a recording. It will include information about the captions (language, etc.), as well as a download link. This may be useful to retrieve live or automatically transcribed subtitles from a recording for manual editing. Get a list of the caption/subtitle files currently available for a recording. It will include information about the captions (language, etc.), as well as a download link. This may be useful to retrieve live or automatically transcribed subtitles from a recording for manual editing.
@ -1059,7 +1061,7 @@ missingParameter
noRecordings noRecordings
: No recording was found matching the provided recording ID. : No recording was found matching the provided recording ID.
### putRecordingTextTrack ### `POST` putRecordingTextTrack
Upload a caption or subtitle file to add it to the recording. If there is any existing track with the same values for kind and lang, it will be replaced. Upload a caption or subtitle file to add it to the recording. If there is any existing track with the same values for kind and lang, it will be replaced.

View File

@ -197,6 +197,9 @@ If `preUploadedPresentationOverrideDefault=false` (or omitted, since `false` is
In BigBlueButton 2.6.17/2.7.5 we added a new configuration property for bbb-apps-akka package under `services` called `checkSumAlgorithmForBreakouts`. By default the value is `"sha256"`. It controls the algorithm for checksum calculation for the breakout rooms join link. In case you overwrite bbb-web's `supportedChecksumAlgorithms` property removing sha256 you will need to set a supported algorithm here too. For example if you want to only use `sha512`, set `supportedChecksumAlgorithms=sha512` in `/etc/bigbluebutton/bbb-web.properties` and also set `checkSumAlgorithmForBreakouts="sha512"` in `/etc/bigbluebutton/bbb-apps-akka.conf` and then restart BigBlueButton. In BigBlueButton 2.6.17/2.7.5 we added a new configuration property for bbb-apps-akka package under `services` called `checkSumAlgorithmForBreakouts`. By default the value is `"sha256"`. It controls the algorithm for checksum calculation for the breakout rooms join link. In case you overwrite bbb-web's `supportedChecksumAlgorithms` property removing sha256 you will need to set a supported algorithm here too. For example if you want to only use `sha512`, set `supportedChecksumAlgorithms=sha512` in `/etc/bigbluebutton/bbb-web.properties` and also set `checkSumAlgorithmForBreakouts="sha512"` in `/etc/bigbluebutton/bbb-apps-akka.conf` and then restart BigBlueButton.
#### Removed support for POST requests on `join` endpoint and Content-Type headers are now required
In BigBlueButton 2.6.18/2.7.8 POST requests are no longer allowed for the `join` endpoint. To ensure they are validated properly, a `Content-Type` header must also be provided for POST requests that contain data in the request body. Endpoints now support a limited set of content types that includes `text/xml`, `application/xml`, `application/x-www-form-url-encoded`, and `multipart/form-data`. By default each endpoint only supports `application/x-www-form-urlencoded` and `multipart/form-data`, but individual endpoints can override this and define their own set of supported content types. The `create` endpoint supports all of the four previously listed content types while `insertDocument` supports only `text/xml` and `application/xml`. Any requests with a content type that differs from the set supported by the target endpoint will be rejected with a new `unsupportedContentType` error.
### Development ### Development