updates to validation on all api endpoints

This commit is contained in:
paultrudel 2021-06-30 10:17:08 -04:00
parent 1191713b48
commit f74ea387d7
43 changed files with 1632 additions and 836 deletions

View File

@ -1,6 +1,6 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.ChecksumValidator;
import org.bigbluebutton.api.model.validator.GetChecksumValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
@ -10,10 +10,10 @@ import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = ChecksumValidator.class)
@Constraint(validatedBy = GetChecksumValidator.class)
@Target(TYPE)
@Retention(RUNTIME)
public @interface ChecksumConstraint {
public @interface GetChecksumConstraint {
String message() default "Invalid checksum: checksums do not match";
Class<?>[] groups() default {};

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.GuestPolicyValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { GuestPolicyValidator.class })
@Target(FIELD)
@Retention(RUNTIME)
public @interface GuestPolicyConstraint {
String message() default "User denied access for this session";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.MaxParticipantsValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { MaxParticipantsValidator.class })
@Target(FIELD)
@Retention(RUNTIME)
public @interface MaxParticipantsConstraint {
String message() default "The maximum number of participants for the meeting has been reached";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.MeetingEndedValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = MeetingEndedValidator.class)
@Target(FIELD)
@Retention(RUNTIME)
public @interface MeetingEndedConstraint {
String message() default "You can not re-join a meeting that has already been forcibly ended";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,22 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.MeetingExistsValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = MeetingExistsValidator.class)
@Target(FIELD)
@Retention(RUNTIME)
public @interface MeetingExistsConstraint {
String message() default "Invalid meeting ID: A meeting with that ID does not exist";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,25 @@
package org.bigbluebutton.api.model.constraint;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@NotEmpty(message = "You must provide a meeting ID")
@Size(min = 2, max = 256, message = "Meeting ID must be between 2 and 256 characters")
@Pattern(regexp = "^[a-zA-Z0-9\\s!@#$%^&*()_\\-+=\\[\\]{};:.'\"<>?\\\\|\\/]+$", message = "Meeting ID cannot contain ','")
@Constraint(validatedBy = {})
@Target(FIELD)
@Retention(RUNTIME)
public @interface MeetingIDConstraint {
String message() default "Invalid meeting ID";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,25 @@
package org.bigbluebutton.api.model.constraint;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@NotEmpty(message = "You must provide a meeting name")
@Size(min = 2, max = 256, message = "Meeting name must be between 2 and 256 characters")
@Pattern(regexp = "^[a-zA-Z0-9\\s!@#$%^&*()_\\-+=\\[\\]{};:.'\"<>?\\\\|\\/]+$", message = "Meeting name cannot contain ','")
@Constraint(validatedBy = {})
@Target(FIELD)
@Retention(RUNTIME)
public @interface MeetingNameConstraint {
String message() default "Invalid meeting name";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.ModeratorPasswordValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = ModeratorPasswordValidator.class)
@Target(TYPE)
@Retention(RUNTIME)
public @interface ModeratorPasswordConstraint {
String message() default "Invalid password: The supplied moderator password is incorrect";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,23 @@
package org.bigbluebutton.api.model.constraint;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@NotEmpty(message = "You must provide your password")
@Size(min = 2, max = 64, message = "Password must be between 8 and 20 characters")
@Constraint(validatedBy = {})
@Target(FIELD)
@Retention(RUNTIME)
public @interface PasswordConstraint {
String message() default "Invalid password";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.PostChecksumValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = PostChecksumValidator.class)
@Target(TYPE)
@Retention(RUNTIME)
public @interface PostChecksumConstraint {
String message() default "Invalid checksum: checksums do not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.api.model.constraint;
import org.bigbluebutton.api.model.validator.UserSessionValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { UserSessionValidator.class })
@Target(FIELD)
@Retention(RUNTIME)
public @interface UserSessionConstraint {
String message() default "Invalid session token";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -1,32 +1,45 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.constraint.MeetingNameConstraint;
import org.bigbluebutton.api.model.constraint.PasswordConstraint;
import org.bigbluebutton.api.model.shared.Checksum;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Map;
public class CreateMeeting extends Request {
public class CreateMeeting extends RequestWithChecksum<CreateMeeting.Params> {
@NotEmpty(message = "You must provide a meeting name")
@Size(min = 2, max = 256, message = "Meeting name must be between 2 and 256 characters")
@Pattern(regexp = "^[a-zA-Z0-9\\s!@#$%^&*()_\\-+=\\[\\]{};:.'\"<>?\\\\|\\/]+$", message = "Meeting name cannot contain ','")
public enum Params implements RequestParameters {
NAME("name"),
MEETING_ID("meetingID"),
VOICE_BRIDGE("voiceBridge"),
ATTENDEE_PW("attendeePW"),
MODERATOR_PW("moderatorPW"),
IS_BREAKOUT_ROOM("isBreakoutRoom"),
RECORD("record");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@MeetingNameConstraint
private String name;
@NotEmpty(message = "You must provide a meeting ID")
@Size(min = 2, max = 256, message = "Meeting ID must be between 2 and 256 characters")
@Pattern(regexp = "^[a-zA-Z0-9\\s!@#$%^&*()_\\-+=\\[\\]{};:.'\"<>?\\\\|\\/]+$", message = "Meeting ID cannot contain ','")
@MeetingIDConstraint
private String meetingID;
@NotEmpty(message = "You must provide a voice bridge")
private String voiceBridge;
@NotEmpty(message = "You must provide an attendee password")
@PasswordConstraint
private String attendeePW;
@NotEmpty(message = "You must provide a moderator password")
@PasswordConstraint
private String moderatorPW;
@NotNull(message = "You must provide whether this meeting is breakout room")
@ -97,12 +110,12 @@ public class CreateMeeting extends Request {
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey("name")) setName(params.get("name")[0]);
if(params.containsKey("meetingID")) setMeetingID(params.get("meetingID")[0]);
if(params.containsKey("voiceBridge")) setVoiceBridge(params.get("voiceBridge")[0]);
if(params.containsKey("attendeePW")) setAttendeePW(params.get("attendeePW")[0]);
if(params.containsKey("moderatorPW")) setModeratorPW(params.get("moderatorPW")[0]);
if(params.containsKey("isBreakoutRoom")) setBreakoutRoom(Boolean.parseBoolean(params.get("isBreakoutRoom")[0]));
if(params.containsKey("record")) setRecord(Boolean.parseBoolean(params.get("record")[0]));
if(params.containsKey(Params.NAME.getValue())) setName(params.get(Params.NAME.getValue())[0]);
if(params.containsKey(Params.MEETING_ID.getValue())) setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
if(params.containsKey(Params.VOICE_BRIDGE.getValue())) setVoiceBridge(params.get(Params.VOICE_BRIDGE.getValue())[0]);
if(params.containsKey(Params.ATTENDEE_PW.getValue())) setAttendeePW(params.get(Params.ATTENDEE_PW.getValue())[0]);
if(params.containsKey(Params.MODERATOR_PW.getValue())) setModeratorPW(params.get(Params.MODERATOR_PW.getValue())[0]);
if(params.containsKey(Params.IS_BREAKOUT_ROOM.getValue())) setBreakoutRoom(Boolean.parseBoolean(params.get(Params.IS_BREAKOUT_ROOM.value)[0]));
if(params.containsKey(Params.RECORD.getValue())) setRecord(Boolean.parseBoolean(params.get(Params.RECORD.getValue())[0]));
}
}

View File

@ -0,0 +1,68 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.constraint.PasswordConstraint;
import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.shared.ModeratorPassword;
import javax.validation.Valid;
import java.util.Map;
public class EndMeeting extends RequestWithChecksum<EndMeeting.Params> {
public enum Params implements RequestParameters {
MEETING_ID("meetingID"),
PASSWORD("password");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@MeetingIDConstraint
@MeetingExistsConstraint
private String meetingID;
@PasswordConstraint
private String password;
@Valid
private ModeratorPassword moderatorPassword;
public EndMeeting(Checksum checksum) {
super(checksum);
moderatorPassword = new ModeratorPassword();
}
public String getMeetingID() {
return meetingID;
}
public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.MEETING_ID.getValue())) {
setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
moderatorPassword.setMeetingID(meetingID);
}
if(params.containsKey(Params.PASSWORD.getValue())) {
setPassword(params.get(Params.PASSWORD.getValue())[0]);
moderatorPassword.setPassword(password);
}
}
}

View File

@ -0,0 +1,52 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.service.SessionService;
import javax.validation.constraints.NotNull;
import java.util.Map;
public class Enter implements Request<Enter.Params> {
public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@NotNull(message = "You must provide a session token")
@UserSessionConstraint
@MaxParticipantsConstraint
@GuestPolicyConstraint
private String sessionToken;
@MeetingExistsConstraint
@MeetingEndedConstraint
private String meetingID;
private SessionService sessionService;
public Enter() {
sessionService = new SessionService();
}
public String getSessionToken() {
return sessionToken;
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.SESSION_TOKEN.getValue())) {
setSessionToken(params.get(Params.SESSION_TOKEN.getValue())[0]);
sessionService.setSessionToken(sessionToken);
meetingID = sessionService.getMeetingID();
}
}
}

View File

@ -0,0 +1,54 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingEndedConstraint;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import org.bigbluebutton.api.service.SessionService;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Map;
public class GuestWait implements Request<GuestWait.Params> {
public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@NotNull(message = "You must provide the session token")
@UserSessionConstraint
private String sessionToken;
@MeetingExistsConstraint
@MeetingEndedConstraint
private String meetingID;
private SessionService sessionService;
public GuestWait() {
sessionService = new SessionService();
}
public String getSessionToken() {
return sessionToken;
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.SESSION_TOKEN.getValue())) {
setSessionToken(params.get(Params.SESSION_TOKEN.getValue())[0]);
sessionService.setSessionToken(sessionToken);
meetingID = sessionService.getMeetingID();
}
}
}

View File

@ -1,18 +1,35 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingEndedConstraint;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.constraint.PasswordConstraint;
import org.bigbluebutton.api.model.shared.Checksum;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Map;
public class JoinMeeting extends Request {
public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
@NotEmpty(message = "You must provide a meeting ID")
@Size(min = 2, max = 256, message = "Meeting ID must be between 2 and 256 characters")
@Pattern(regexp = "^[a-zA-Z0-9\\s!@#$%^&*()_\\-+=\\[\\]{};:.'\"<>?\\\\|\\/]+$", message = "Meeting name cannot contain ','")
public enum Params implements RequestParameters {
MEETING_ID("meetingID"),
USER_ID("userID"),
FULL_NAME("fullName"),
PASSWORD("password"),
GUEST("guest"),
AUTH("auth"),
CREATE_TIME("createTime");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@MeetingIDConstraint
@MeetingExistsConstraint
@MeetingEndedConstraint
private String meetingID;
private String userID;
@ -20,8 +37,7 @@ public class JoinMeeting extends Request {
@NotEmpty(message = "You must provide your name")
private String fullName;
@NotEmpty(message = "You must provide your password")
@Size(min = 2, max = 64, message = "Password must be between 8 and 20 characters")
@PasswordConstraint
private String password;
private Boolean guest;
@ -92,12 +108,15 @@ public class JoinMeeting extends Request {
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey("meetingID")) setMeetingID(params.get("meetingID")[0]);
if(params.containsKey("userID")) setUserID(params.get("userID")[0]);
if(params.containsKey("fullName")) setFullName(params.get("fullName")[0]);
if(params.containsKey("password")) setPassword(params.get("password")[0]);
if(params.containsKey("guest")) setGuest(Boolean.parseBoolean(params.get("guest")[0]));
if(params.containsKey("auth")) setAuth(Boolean.parseBoolean(params.get("auth")[0]));
if(params.containsKey("createTime")) setCreateTime(Long.parseLong(params.get("createTime")[0]));
if(params.containsKey(Params.MEETING_ID.getValue())) {
setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
}
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]);
if(params.containsKey(Params.GUEST.getValue())) setGuest(Boolean.parseBoolean(params.get(Params.GUEST.getValue())[0]));
if(params.containsKey(Params.AUTH.getValue())) setAuth(Boolean.parseBoolean(params.get(Params.AUTH.getValue())[0]));
if(params.containsKey(Params.CREATE_TIME.getValue())) setCreateTime(Long.parseLong(params.get(Params.CREATE_TIME.getValue())[0]));
}
}

View File

@ -0,0 +1,43 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.shared.Checksum;
import java.util.Map;
public class MeetingInfo extends RequestWithChecksum<MeetingInfo.Params> {
public enum Params implements RequestParameters {
MEETING_ID("meetingID");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@MeetingIDConstraint
@MeetingExistsConstraint
private String meetingID;
public MeetingInfo(Checksum checksum) {
super(checksum);
}
public String getMeetingID() {
return meetingID;
}
public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.MEETING_ID.getValue())) {
setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
}
}
}

View File

@ -0,0 +1,39 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.shared.Checksum;
import java.util.Map;
public class MeetingRunning extends RequestWithChecksum<MeetingRunning.Params> {
public enum Params implements RequestParameters {
MEETING_ID("meetingID");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@MeetingIDConstraint
private String meetingID;
public MeetingRunning(Checksum checksum) {
super(checksum);
}
public String getMeetingID() {
return meetingID;
}
public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.MEETING_ID.getValue())) setMeetingID(params.get(Params.MEETING_ID.getValue())[0]);
}
}

View File

@ -1,26 +1,8 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.shared.Checksum;
import javax.validation.Valid;
import java.util.Map;
public abstract class Request {
public interface Request<P extends Enum<P> & RequestParameters> {
@Valid
protected Checksum checksum;
protected Request(Checksum checksum) {
this.checksum = checksum;
}
public Checksum getChecksum() {
return checksum;
}
public void setChecksum(Checksum checksum) {
this.checksum = checksum;
}
public abstract void populateFromParamsMap(Map<String, String[]> params);
void populateFromParamsMap(Map<String, String[]> params);
}

View File

@ -0,0 +1,6 @@
package org.bigbluebutton.api.model.request;
public interface RequestParameters {
String getValue();
}

View File

@ -0,0 +1,26 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.shared.Checksum;
import javax.validation.Valid;
import java.util.Map;
public abstract class RequestWithChecksum<P extends Enum<P> & RequestParameters> implements Request<P> {
@Valid
protected Checksum checksum;
protected RequestWithChecksum(Checksum checksum) {
this.checksum = checksum;
}
public Checksum getChecksum() {
return checksum;
}
public void setChecksum(Checksum checksum) {
this.checksum = checksum;
}
public abstract void populateFromParamsMap(Map<String, String[]> params);
}

View File

@ -0,0 +1,58 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.model.constraint.MeetingIDConstraint;
import org.bigbluebutton.api.model.shared.Checksum;
import javax.validation.constraints.NotEmpty;
import java.util.Map;
public class SetPollXML extends RequestWithChecksum<SetPollXML.Params> {
public enum Params implements RequestParameters {
MEETING_ID("meetingID"),
POLL_XML("pollXML");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@MeetingIDConstraint
@MeetingExistsConstraint
private String meetingID;
@NotEmpty(message = "You must provide the poll")
private String pollXML;
public SetPollXML(Checksum checksum) {
super(checksum);
}
public String getMeetingID() {
return meetingID;
}
public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}
public String getPollXML() {
return pollXML;
}
public void setPollXML(String pollXML) {
this.pollXML = pollXML;
}
@Override
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.POLL_XML.getValue())) setPollXML(params.get(Params.POLL_XML.getValue())[0]);
}
}

View File

@ -0,0 +1,36 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import javax.validation.constraints.NotNull;
import java.util.Map;
public class SignOut implements Request<SignOut.Params> {
public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@NotNull(message = "You must provide a session token")
@UserSessionConstraint
private String sessionToken;
public String getSessionToken() {
return sessionToken;
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Enter.Params.SESSION_TOKEN.getValue())) setSessionToken(params.get(Enter.Params.SESSION_TOKEN.getValue())[0]);
}
}

View File

@ -0,0 +1,25 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.shared.Checksum;
import java.util.Map;
public class SimpleRequest extends RequestWithChecksum<SimpleRequest.Params> {
public enum Params implements RequestParameters {
NONE("none");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
public SimpleRequest(Checksum checksum) {
super(checksum);
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {}
}

View File

@ -0,0 +1,50 @@
package org.bigbluebutton.api.model.request;
import org.bigbluebutton.api.model.constraint.*;
import org.bigbluebutton.api.service.SessionService;
import javax.validation.constraints.NotNull;
import java.util.Map;
public class Stuns implements Request<Stuns.Params> {
public enum Params implements RequestParameters {
SESSION_TOKEN("sessionToken");
private final String value;
Params(String value) { this.value = value; }
public String getValue() { return value; }
}
@NotNull(message = "You must provide a session token")
@UserSessionConstraint
private String sessionToken;
@MeetingExistsConstraint
@MeetingEndedConstraint
private String meetingID;
private SessionService sessionService;
public Stuns() { sessionService = new SessionService(); }
public String getSessionToken() {
return sessionToken;
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Enter.Params.SESSION_TOKEN.getValue())) {
setSessionToken(params.get(Enter.Params.SESSION_TOKEN.getValue())[0]);
sessionService.setSessionToken(sessionToken);
meetingID = sessionService.getMeetingID();
}
}
}

View File

@ -1,48 +1,22 @@
package org.bigbluebutton.api.model.shared;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.bigbluebutton.api.model.constraint.ChecksumConstraint;
import org.bigbluebutton.api.util.ParamsUtil;
import javax.validation.constraints.NotEmpty;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
@ChecksumConstraint(message = "Checksums do not match")
public class Checksum {
public abstract class Checksum {
@NotEmpty(message = "You must provide the API call")
private String apiCall;
protected String apiCall;
@NotEmpty(message = "You must provide the checksum")
private String checksum;
protected String checksum;
@NotEmpty(message = "You must provide the query string")
private String queryString;
protected String queryStringWithoutChecksum;
private String queryStringWithoutChecksum;
public Checksum(String apiCall, String checksum, String queryString) {
public Checksum(String apiCall, String checksum) {
this.apiCall = ParamsUtil.sanitizeString(apiCall);
this.checksum = ParamsUtil.sanitizeString(checksum);
this.queryString = ParamsUtil.sanitizeString(queryString);
removeChecksumFromQueryString();
}
private void removeChecksumFromQueryString() {
List<NameValuePair> params = URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8);
Iterator<NameValuePair> itr = params.iterator();
while(itr.hasNext()) {
NameValuePair pair = itr.next();
if(pair.getName().equals("checksum")) {
itr.remove();
}
}
queryStringWithoutChecksum = URLEncodedUtils.format(params, '&', StandardCharsets.UTF_8);
}
public String getApiCall() {
@ -61,14 +35,6 @@ public class Checksum {
this.checksum = checksum;
}
public String getQueryString() {
return queryString;
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
public String getQueryStringWithoutChecksum() {
return queryStringWithoutChecksum;
}

View File

@ -0,0 +1,46 @@
package org.bigbluebutton.api.model.shared;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.bigbluebutton.api.model.constraint.GetChecksumConstraint;
import org.bigbluebutton.api.util.ParamsUtil;
import javax.validation.constraints.NotEmpty;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
@GetChecksumConstraint(message = "Checksums do not match")
public class GetChecksum extends Checksum {
@NotEmpty(message = "You must provide the query string")
private String queryString;
public GetChecksum(String apiCall, String checksum, String queryString) {
super(apiCall, checksum);
this.queryString = ParamsUtil.sanitizeString(queryString);
removeChecksumFromQueryString();
}
private void removeChecksumFromQueryString() {
List<NameValuePair> params = URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8);
Iterator<NameValuePair> itr = params.iterator();
while(itr.hasNext()) {
NameValuePair pair = itr.next();
if(pair.getName().equals("checksum")) {
itr.remove();
}
}
queryStringWithoutChecksum = URLEncodedUtils.format(params, '&', StandardCharsets.UTF_8);
}
public String getQueryString() {
return queryString;
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
}

View File

@ -0,0 +1,31 @@
package org.bigbluebutton.api.model.shared;
import org.bigbluebutton.api.model.constraint.ModeratorPasswordConstraint;
import javax.validation.constraints.NotEmpty;
@ModeratorPasswordConstraint(message = "Provided moderator password is incorrect")
public class ModeratorPassword {
@NotEmpty(message = "You must provide the meeting ID")
private String meetingID;
@NotEmpty(message = "You must provide the password for the call")
private String password;
public String getMeetingID() {
return meetingID;
}
public void setMeetingID(String meetingID) {
this.meetingID = meetingID;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,67 @@
package org.bigbluebutton.api.model.shared;
import org.bigbluebutton.api.model.constraint.PostChecksumConstraint;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
@PostChecksumConstraint(message = "Checksums do not match")
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;
buildQueryStringFromParamsMap();
}
private void buildQueryStringFromParamsMap() {
StringBuilder queryString = new StringBuilder();
SortedSet<String> keys = new TreeSet<>(params.keySet());
boolean firstParam = true;
for(String key: keys) {
if(key.equals("checksum")) {
continue;
}
for(String value: params.get(key)) {
if(firstParam) {
firstParam = false;
} else {
queryString.append("&");
}
queryString.append(key);
queryString.append("=");
String encodedValue = encodeString(value);
queryString.append(encodedValue);
}
}
queryStringWithoutChecksum = queryString.toString();
}
private String encodeString(String stringToEncode) {
String encodedResult;
try {
encodedResult = URLEncoder.encode(stringToEncode, StandardCharsets.UTF_8.name());
} catch(UnsupportedEncodingException ex) {
encodedResult = stringToEncode;
}
return encodedResult;
}
public Map<String, String[]> getParams() { return params; }
public void setParams(Map<String, String[]> params) { this.params = params; }
}

View File

@ -4,31 +4,42 @@ import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.apache.commons.codec.digest.DigestUtils;
import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.constraint.ChecksumConstraint;
import org.bigbluebutton.api.service.ValidatorService;
import org.bigbluebutton.api.model.constraint.GetChecksumConstraint;
import org.bigbluebutton.api.model.shared.GetChecksum;
import org.bigbluebutton.api.service.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ChecksumValidator implements ConstraintValidator<ChecksumConstraint, Checksum> {
public class GetChecksumValidator implements ConstraintValidator<GetChecksumConstraint, GetChecksum> {
private static Logger log = LoggerFactory.getLogger(ChecksumValidator.class);
private static Logger log = LoggerFactory.getLogger(GetChecksumValidator.class);
@Override
public void initialize(ChecksumConstraint checksumConstraint) {}
public void initialize(GetChecksumConstraint checksumConstraint) {}
@Override
public boolean isValid(Checksum checksum, ConstraintValidatorContext context) {
String securitySalt = ValidatorService.getSecuritySalt();
public boolean isValid(GetChecksum checksum, ConstraintValidatorContext context) {
String securitySalt = ServiceUtils.getValidationService().getSecuritySalt();
log.info("Security salt: {}", securitySalt);
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);

View File

@ -0,0 +1,33 @@
package org.bigbluebutton.api.model.validator;
import org.bigbluebutton.api.MeetingService;
import org.bigbluebutton.api.domain.GuestPolicy;
import org.bigbluebutton.api.domain.UserSession;
import org.bigbluebutton.api.model.constraint.GuestPolicyConstraint;
import org.bigbluebutton.api.service.ServiceUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class GuestPolicyValidator implements ConstraintValidator<GuestPolicyConstraint, String> {
@Override
public void initialize(GuestPolicyConstraint constraintAnnotation) {}
@Override
public boolean isValid(String sessionToken, ConstraintValidatorContext constraintValidatorContext) {
if(sessionToken == null) {
return false;
}
MeetingService meetingService = ServiceUtils.getMeetingService();
UserSession userSession = meetingService.getUserSessionWithAuthToken(sessionToken);
if(userSession == null || userSession.guestStatus.equals(GuestPolicy.DENY)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,51 @@
package org.bigbluebutton.api.model.validator;
import org.bigbluebutton.api.MeetingService;
import org.bigbluebutton.api.domain.Meeting;
import org.bigbluebutton.api.domain.UserSession;
import org.bigbluebutton.api.model.constraint.MaxParticipantsConstraint;
import org.bigbluebutton.api.service.ServiceUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MaxParticipantsValidator implements ConstraintValidator<MaxParticipantsConstraint, String> {
@Override
public void initialize(MaxParticipantsConstraint constraintAnnotation) {}
@Override
public boolean isValid(String sessionToken, ConstraintValidatorContext constraintValidatorContext) {
if(sessionToken == null) {
return false;
}
MeetingService meetingService = ServiceUtils.getMeetingService();
UserSession userSession = meetingService.getUserSessionWithAuthToken(sessionToken);
if(userSession == null) {
return false;
}
Meeting meeting = meetingService.getMeeting(userSession.meetingID);
if(meeting == null) {
return false;
}
int maxParticipants = meeting.getMaxUsers();
boolean enabled = maxParticipants > 0;
boolean rejoin = meeting.getUserById(userSession.internalUserId) != null;
boolean reenter = meeting.getEnteredUserById(userSession.internalUserId) != null;
int joinedUsers = meeting.getUsers().size();
int enteredUsers = meeting.getEnteredUsers().size();
boolean reachedMax = (joinedUsers + enteredUsers) >= maxParticipants;
if(enabled && !rejoin && !reenter && reachedMax) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,34 @@
package org.bigbluebutton.api.model.validator;
import org.bigbluebutton.api.domain.Meeting;
import org.bigbluebutton.api.model.constraint.MeetingEndedConstraint;
import org.bigbluebutton.api.service.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MeetingEndedValidator implements ConstraintValidator<MeetingEndedConstraint, String> {
private static Logger log = LoggerFactory.getLogger(MeetingEndedValidator.class);
@Override
public void initialize(MeetingEndedConstraint constraintAnnotation) {}
@Override
public boolean isValid(String meetingID, ConstraintValidatorContext context) {
if(meetingID == null) {
return false;
}
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(meetingID);
if(meeting == null) {
return false;
}
return !meeting.isForciblyEnded();
}
}

View File

@ -0,0 +1,36 @@
package org.bigbluebutton.api.model.validator;
import org.bigbluebutton.api.domain.Meeting;
import org.bigbluebutton.api.model.constraint.MeetingExistsConstraint;
import org.bigbluebutton.api.service.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MeetingExistsValidator implements ConstraintValidator<MeetingExistsConstraint, String> {
private static Logger log = LoggerFactory.getLogger(MeetingExistsValidator.class);
@Override
public void initialize(MeetingExistsConstraint constraintAnnotation) {}
@Override
public boolean isValid(String meetingID, ConstraintValidatorContext context) {
log.info("Validating existence of meeting with ID {}", meetingID);
if(meetingID == null) {
return false;
}
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(meetingID);
if(meeting == null) {
log.info("meetingExistsError: No meeting with the given ID {} could be found", meetingID);
return false;
}
return true;
}
}

View File

@ -0,0 +1,52 @@
package org.bigbluebutton.api.model.validator;
import org.bigbluebutton.api.domain.Meeting;
import org.bigbluebutton.api.model.constraint.ModeratorPasswordConstraint;
import org.bigbluebutton.api.model.shared.ModeratorPassword;
import org.bigbluebutton.api.service.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ModeratorPasswordValidator implements ConstraintValidator<ModeratorPasswordConstraint, ModeratorPassword> {
private static Logger log = LoggerFactory.getLogger(MeetingExistsValidator.class);
@Override
public void initialize(ModeratorPasswordConstraint constraintAnnotation) {}
@Override
public boolean isValid(ModeratorPassword moderatorPassword, ConstraintValidatorContext context) {
log.info("Validating password {} for meeting with ID {}",
moderatorPassword.getPassword(), moderatorPassword.getMeetingID());
if(moderatorPassword.getMeetingID() == null) {
return false;
}
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(moderatorPassword.getMeetingID());
if(meeting == null) {
return false;
}
String actualPassword = meeting.getModeratorPassword();
String providedPassword = moderatorPassword.getPassword();
if(providedPassword == null) {
return false;
}
log.info("Actual password: {}", actualPassword);
log.info("Provided password: {}", providedPassword);
if(!providedPassword.equals(actualPassword)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,53 @@
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.equals(checksum)) {
log.info("checksumError: failed checksum. our checksum: [{}], client: [{}]", createdCheckSum, checksum);
return false;
}
return true;
}
}

View File

@ -0,0 +1,25 @@
package org.bigbluebutton.api.model.validator;
import org.bigbluebutton.api.domain.UserSession;
import org.bigbluebutton.api.model.constraint.UserSessionConstraint;
import org.bigbluebutton.api.service.ServiceUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class UserSessionValidator implements ConstraintValidator<UserSessionConstraint, String> {
@Override
public void initialize(UserSessionConstraint constraintAnnotation) {}
@Override
public boolean isValid(String sessionToken, ConstraintValidatorContext constraintValidatorContext) {
UserSession userSession = ServiceUtils.getMeetingService().getUserSessionWithAuthToken(sessionToken);
if(userSession == null) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,39 @@
package org.bigbluebutton.api.service;
import org.bigbluebutton.api.MeetingService;
import org.bigbluebutton.api.ParamsProcessorUtil;
import org.bigbluebutton.api.domain.Meeting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServiceUtils {
private static Logger log = LoggerFactory.getLogger(ServiceUtils.class);
private static MeetingService meetingService;
private static ValidationService validationService;
public void setMeetingService(MeetingService meetingService) { this.meetingService = meetingService; }
public static MeetingService getMeetingService() { return meetingService; }
public void setValidationService(ValidationService validationService) { this.validationService = validationService; }
public static ValidationService getValidationService() { return validationService; }
public static Meeting findMeetingFromMeetingID(String meetingID) {
log.info("Attempting to find meeting with ID {}", meetingID);
Meeting meeting = meetingService.getMeeting(meetingID);
if(meeting == null) {
log.info("Meeting with ID {} could not be found", meetingID);
log.info("Provided ID {} may be an external ID converting to an internal ID", meetingID);
ParamsProcessorUtil paramsProcessorUtil = new ParamsProcessorUtil();
String internalMeetingID = paramsProcessorUtil.convertToInternalMeetingId(meetingID);
log.info("Provided ID {} converted to internal ID {}", meetingID, internalMeetingID);
meeting = meetingService.getMeeting(internalMeetingID);
}
return meeting;
}
}

View File

@ -0,0 +1,35 @@
package org.bigbluebutton.api.service;
import org.bigbluebutton.api.MeetingService;
import org.bigbluebutton.api.domain.UserSession;
public class SessionService {
private String sessionToken;
private UserSession userSession;
private MeetingService meetingService;
public SessionService() {
meetingService = ServiceUtils.getMeetingService();
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
getUserSessionWithToken();
}
public String getSessionToken() { return sessionToken; }
private void getUserSessionWithToken() {
if(sessionToken != null) {
userSession = meetingService.getUserSessionWithAuthToken(sessionToken);
}
}
public String getMeetingID() {
if(userSession != null) {
return userSession.meetingID;
}
return "";
}
}

View File

@ -0,0 +1,199 @@
package org.bigbluebutton.api.service;
import org.bigbluebutton.api.model.request.*;
import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.model.shared.GetChecksum;
import org.bigbluebutton.api.model.shared.PostChecksum;
import org.bigbluebutton.api.util.ParamsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.*;
public class ValidationService {
private static Logger log = LoggerFactory.getLogger(ValidationService.class);
public enum RequestType {
GET,
POST
}
public enum ApiCall {
CREATE("create", RequestType.GET),
JOIN("join", RequestType.GET),
MEETING_RUNNING("isMeetingRunning", RequestType.GET),
END("end", RequestType.GET),
GET_MEETING_INFO("getMeetingInfo", RequestType.GET),
GET_MEETINGS("getMeetings", RequestType.GET),
GET_SESSIONS("getSessions", RequestType.GET),
SET_POLL_XML("setPollXML", RequestType.POST),
GUEST_WAIT("guestWait", RequestType.GET),
ENTER("enter", RequestType.GET),
STUNS("stuns", RequestType.GET),
SIGN_OUT("signOut", RequestType.GET);
private final String name;
private final RequestType requestType;
ApiCall(String name, RequestType requestType) {
this.name = name;
this.requestType = requestType;
}
public String getName() { return this.name; }
public RequestType getRequestType() { return this.requestType; }
}
private String securitySalt;
private Boolean allowRequestsWithoutSession;
private ValidatorFactory validatorFactory;
private Validator validator;
public ValidationService() {
validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
public Set<String> validate(ApiCall apiCall, Map<String, String[]> params, String queryString) {
log.info("Validating {} request with query string {}", apiCall.getName(), queryString);
// log.info("Request has params: {}", mapToString(params));
params = sanitizeParams(params);
Request request = initializeRequest(apiCall, params, queryString);
Set<String> violations = new HashSet<>();
if(request == null) {
violations.add("validationError: Request not recognized");
} else {
request.populateFromParamsMap(params);
violations = performValidation(request);
}
return violations;
}
private Request initializeRequest(ApiCall apiCall, Map<String, String[]> params, String queryString) {
Request request = null;
Checksum checksum;
String checksumValue = "";
if(params.containsKey("checksum")) {
checksumValue = params.get("checksum")[0];
}
switch(apiCall.requestType) {
case GET:
checksum = new GetChecksum(apiCall.getName(), checksumValue, queryString);
switch(apiCall) {
case CREATE:
request = new CreateMeeting(checksum);
break;
case JOIN:
request = new JoinMeeting(checksum);
break;
case MEETING_RUNNING:
request = new MeetingRunning(checksum);
break;
case END:
request = new EndMeeting(checksum);
break;
case GET_MEETING_INFO:
request = new MeetingInfo(checksum);
break;
case SET_POLL_XML:
request = new SetPollXML(checksum);
break;
case GET_MEETINGS:
case GET_SESSIONS:
request = new SimpleRequest(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 POST:
checksum = new PostChecksum(apiCall.getName(), checksumValue, params);
switch(apiCall) {
case SET_POLL_XML:
request = new SetPollXML(checksum);
}
}
return request;
}
private <R extends Request> Set<String> performValidation(R classToValidate) {
Set<ConstraintViolation<R>> violations = validator.validate(classToValidate);
Set<String> violationSet = new HashSet<>();
for(ConstraintViolation<R> violation: violations) {
violationSet.add(violation.getMessage());
}
return violationSet;
}
private Map<String, String[]> sanitizeParams(Map<String, String[]> params) {
Map<String, String[]> sanitizedParams = new LinkedHashMap<>();
for(Map.Entry<String, String[]> param: params.entrySet()) {
String paramName = ParamsUtil.sanitizeString(param.getKey());
String[] sanitizedValues = new String[param.getValue().length];
for(int i = 0; i < sanitizedValues.length; i++) {
String sanitizedValue = ParamsUtil.sanitizeString(param.getValue()[i]);
sanitizedValues[i] = sanitizedValue;
}
sanitizedParams.put(paramName, sanitizedValues);
}
return sanitizedParams;
}
private String mapToString(Map<String, String[]> map) {
StringBuilder mapString = new StringBuilder();
for(Map.Entry<String, String[]> entry: map.entrySet()) {
StringBuilder entryString = new StringBuilder();
entryString.append(entry.getKey() + ": [");
for(int i = 0; i < entry.getValue().length; i++) {
if(i == entry.getValue().length - 1) {
entryString.append(entry.getValue()[i]);
} else {
entryString.append(entry.getValue()[i] + ", ");
}
}
entryString.append("], ");
mapString.append(entryString);
}
return mapString.toString();
}
public void setSecuritySalt(String securitySalt) { this.securitySalt = securitySalt; }
public String getSecuritySalt() { return securitySalt; }
public void setAllowRequestsWithoutSession(Boolean allowRequestsWithoutSession) {
this.allowRequestsWithoutSession = allowRequestsWithoutSession;
}
public Boolean getAllowRequestsWithoutSession() { return allowRequestsWithoutSession; }
}

View File

@ -1,108 +0,0 @@
package org.bigbluebutton.api.service;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.bigbluebutton.api.model.request.CreateMeeting;
import org.bigbluebutton.api.model.request.JoinMeeting;
import org.bigbluebutton.api.model.request.Request;
import org.bigbluebutton.api.model.shared.Checksum;
import org.bigbluebutton.api.util.ParamsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class ValidatorService {
private static Logger log = LoggerFactory.getLogger(ValidatorService.class);
public static enum ApiCall {
CREATE("create"),
JOIN("join");
private final String name;
private ApiCall(String name) {
this.name = name;
}
public String getName() { return this.name; }
}
private static String securitySalt;
private ValidatorFactory validatorFactory;
private Validator validator;
public ValidatorService() {
validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
public Set<String> validate(ApiCall apiCall, Map<String, String[]> params, String queryString) {
log.info("Validating {} request with parameters {}", apiCall.getName(), queryString);
params = sanitizeParams(params);
Checksum checksum = new Checksum(apiCall.getName(), params.get("checksum")[0], queryString);
Request request = null;
Set<String> violations = new HashSet<>();
switch(apiCall) {
case CREATE:
request = new CreateMeeting(checksum);
break;
case JOIN:
request = new JoinMeeting(checksum);
break;
default:
violations.add("validationError: Request not recognized");
break;
}
if(request != null) {
request.populateFromParamsMap(params);
violations = performValidation(request);
}
return violations;
}
private <R extends Request> Set<String> performValidation(R classToValidate) {
Set<ConstraintViolation<R>> violations = validator.validate(classToValidate);
Set<String> violationSet = new HashSet<>();
for(ConstraintViolation<R> violation: violations) {
violationSet.add(violation.getMessage());
}
return violationSet;
}
private Map<String, String[]> sanitizeParams(Map<String, String[]> params) {
Map<String, String[]> sanitizedParams = new LinkedHashMap<>();
for(Map.Entry<String, String[]> param: params.entrySet()) {
String paramName = ParamsUtil.sanitizeString(param.getKey());
String[] sanitizedValues = new String[param.getValue().length];
for(int i = 0; i < sanitizedValues.length; i++) {
String sanitziedValue = ParamsUtil.sanitizeString(param.getValue()[i]);
sanitizedValues[i] = sanitziedValue;
}
sanitizedParams.put(paramName, sanitizedValues);
}
return sanitizedParams;
}
public void setSecuritySalt(String securitySalt) { this.securitySalt = securitySalt; }
public static String getSecuritySalt() { return securitySalt; }
}

View File

@ -26,6 +26,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean id="characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter">
<property name="encoding">
@ -115,8 +116,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="clientConfigServiceHelper" ref="configServiceHelper"/>
</bean>
<bean id="validatorService" class="org.bigbluebutton.api.service.ValidatorService">
<bean id="validationService" class="org.bigbluebutton.api.service.ValidationService">
<property name="securitySalt" value="${securitySalt}"/>
<property name="allowRequestsWithoutSession" value="${allowRequestsWithoutSession}"/>
</bean>
<bean id="serviceUtils" class="org.bigbluebutton.api.service.ServiceUtils">
<property name="meetingService" ref="meetingService" />
<property name="validationService" ref="validationService" />
</bean>
<bean id="paramsProcessorUtil" class="org.bigbluebutton.api.ParamsProcessorUtil">

View File

@ -30,7 +30,7 @@ import org.bigbluebutton.api.domain.Config
import org.bigbluebutton.api.domain.GuestPolicy
import org.bigbluebutton.api.domain.Meeting
import org.bigbluebutton.api.domain.UserSession
import org.bigbluebutton.api.service.ValidatorService
import org.bigbluebutton.api.service.ValidationService
import org.bigbluebutton.api.util.ParamsUtil
import org.bigbluebutton.api.util.ResponseBuilder
import org.bigbluebutton.presentation.PresentationUrlDownloadService
@ -61,7 +61,7 @@ class ApiController {
StunTurnService stunTurnService
HTML5LoadBalancingService html5LoadBalancingService
ResponseBuilder responseBuilder = initResponseBuilder()
ValidatorService validatorService
ValidationService validationService
def initResponseBuilder = {
String protocol = this.getClass().getResource("").getProtocol();
@ -97,67 +97,17 @@ class ApiController {
log.debug request.getParameterMap().toMapString()
log.debug request.getQueryString()
Set<String> violations = validatorService.validate(
ValidatorService.ApiCall.CREATE,
String validationResponse = validateRequest(
ValidationService.ApiCall.CREATE,
request.getParameterMap(),
request.getQueryString()
)
if(!violations.isEmpty()) {
StringBuilder violationMessages = new StringBuilder()
for(String violation: violations) {
violationMessages.append(violation + "\n");
log.error violation
}
invalid("validationError", violationMessages.toString())
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
//sanitizeInput
// params.each {
// key, value -> params[key] = sanitizeInput(value)
// }
//
// // BEGIN - backward compatibility
// if (StringUtils.isEmpty(params.checksum)) {
// invalid("checksumError", "You did not pass the checksum security check")
// return
// }
//
// if (!StringUtils.isEmpty(params.meetingID)) {
// params.meetingID = StringUtils.strip(params.meetingID);
// if (StringUtils.isEmpty(params.meetingID)) {
// invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
// return
// }
// } else {
// invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
// return
// }
//
// if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
// invalid("checksumError", "You did not pass the checksum security check")
// return
// }
// // END - backward compatibility
//
// ApiErrors errors = new ApiErrors();
// paramsProcessorUtil.processRequiredCreateParams(params, errors);
//
// if (errors.hasErrors()) {
// respondWithErrors(errors)
// return
// }
//
// // Do we agree with the checksum? If not, complain.
// if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
// errors.checksumError()
// respondWithErrors(errors)
// return
// }
// Ensure unique TelVoice. Uniqueness is not guaranteed by paramsProcessorUtil.
if (!params.voiceBridge) {
// Try up to 10 times. We should find a valid telVoice quickly unless
@ -227,77 +177,17 @@ class ApiController {
log.debug request.getParameterMap().toMapString()
log.debug request.getQueryString()
Set<String> violations = validatorService.validate(
ValidatorService.ApiCall.JOIN,
String validationResponse = validateRequest(
ValidationService.ApiCall.JOIN,
request.getParameterMap(),
request.getQueryString()
)
if(!violations.isEmpty()) {
StringBuilder violationMessages = new StringBuilder()
for(String violation: violations) {
violationMessages.append(violation + "\n");
log.error violation
}
invalid("validationError", violationMessages.toString())
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse, REDIRECT_RESPONSE)
return
}
// ApiErrors errors = new ApiErrors()
//
// //sanitizeInput
// params.each {
// key, value -> params[key] = sanitizeInput(value)
// }
//
// // BEGIN - backward compatibility
// if (StringUtils.isEmpty(params.checksum)) {
// invalid("checksumError", "You did not pass the checksum security check")
// return
// }
//
// if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
// invalid("checksumError", "You did not pass the checksum security check")
// return
// }
//
// //checking for an empty username or for a username containing whitespaces only
// if (!StringUtils.isEmpty(params.fullName)) {
// params.fullName = StringUtils.strip(params.fullName);
// if (StringUtils.isEmpty(params.fullName)) {
// invalid("missingParamFullName", "You must specify a name for the attendee who will be joining the meeting.", REDIRECT_RESPONSE);
// return
// }
// } else {
// invalid("missingParamFullName", "You must specify a name for the attendee who will be joining the meeting.", REDIRECT_RESPONSE);
// return
// }
//
// if (!StringUtils.isEmpty(params.meetingID)) {
// params.meetingID = StringUtils.strip(params.meetingID);
// if (StringUtils.isEmpty(params.meetingID)) {
// invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.", REDIRECT_RESPONSE)
// return
// }
// } else {
// invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.", REDIRECT_RESPONSE)
// return
// }
//
// if (StringUtils.isEmpty(params.password)) {
// invalid("invalidPassword", "You either did not supply a password or the password supplied is neither the attendee or moderator password for this conference.", REDIRECT_RESPONSE);
// return
// }
//
// // END - backward compatibility
//
// // Do we have a checksum? If none, complain.
// if (StringUtils.isEmpty(params.checksum)) {
// errors.missingParamError("checksum");
// }
Boolean authenticated = false;
Boolean guest = false;
@ -314,60 +204,11 @@ class ApiController {
authenticated = Boolean.parseBoolean(params.auth)
}
// Do we have a name for the user joining? If none, complain.
if (!StringUtils.isEmpty(params.fullName)) {
if (StringUtils.isEmpty(params.fullName)) {
errors.missingParamError("fullName");
}
} else {
errors.missingParamError("fullName");
}
String fullName = ParamsUtil.stripControlChars(params.fullName)
// Do we have a meeting id? If none, complain.
// if (!StringUtils.isEmpty(params.meetingID)) {
// params.meetingID = StringUtils.strip(params.meetingID);
// if (StringUtils.isEmpty(params.meetingID)) {
// errors.missingParamError("meetingID");
// }
// } else {
// errors.missingParamError("meetingID");
// }
// if (!StringUtils.isEmpty(params.fullName)) {
// if (StringUtils.isEmpty(params.fullName)) {
// errors.missingParamError("fullName");
// }
// } else {
// errors.missingParamError("fullName");
// }
//
// // Do we have a meeting id? If none, complain.
// if (!StringUtils.isEmpty(params.meetingID)) {
// params.meetingID = StringUtils.strip(params.meetingID);
// if (StringUtils.isEmpty(params.meetingID)) {
// errors.missingParamError("meetingID");
// }
// } else {
// errors.missingParamError("meetingID");
// }
String externalMeetingId = params.meetingID
// Do we have a password? If not, complain.
String attPW = params.password
// if (StringUtils.isEmpty(attPW)) {
// errors.missingParamError("password");
// }
// Do we agree on the checksum? If not, complain.
// if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
// errors.checksumError()
// }
//
// if (errors.hasErrors()) {
// respondWithErrors(errors, REDIRECT_RESPONSE)
// return
// }
// Everything is good so far. Translate the external meeting id to an internal meeting id. If
// we can't find the meeting, complain.
@ -375,16 +216,6 @@ class ApiController {
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId);
if (meeting == null) {
// BEGIN - backward compatibility
invalid("invalidMeetingIdentifier", "The meeting ID that you supplied did not match any existing meetings", REDIRECT_RESPONSE);
return
// END - backward compatibility
errors.invalidMeetingIdError();
respondWithErrors(errors, REDIRECT_RESPONSE)
return
}
// the createTime mismatch with meeting's createTime, complain
// In the future, the createTime param will be required
@ -408,18 +239,6 @@ class ApiController {
}
}
// Is this user joining a meeting that has been ended. If so, complain.
if (meeting.isForciblyEnded()) {
// BEGIN - backward compatibility
invalid("meetingForciblyEnded", "You can not re-join a meeting that has already been forcibly ended. However, once the meeting is removed from memory (according to the timeout configured on this server, you will be able to once again create a meeting with the same meeting ID", REDIRECT_RESPONSE);
return
// END - backward compatibility
errors.meetingForciblyEndedError();
respondWithErrors(errors, REDIRECT_RESPONSE)
return
}
// Now determine if this user is a moderator or a viewer.
String role = null;
if (meeting.getModeratorPassword().equals(attPW)) {
@ -596,66 +415,22 @@ class ApiController {
def isMeetingRunning = {
String API_CALL = 'isMeetingRunning'
log.debug CONTROLLER_NAME + "#${API_CALL}"
log.debug request.getParameterMap().toMapString()
log.debug request.getQueryString()
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String validationResponse = validateRequest(
ValidationService.ApiCall.MEETING_RUNNING,
request.getParameterMap(),
request.getQueryString()
)
// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
} else {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility
ApiErrors errors = new ApiErrors()
// Do we have a checksum? If none, complain.
if (StringUtils.isEmpty(params.checksum)) {
errors.missingParamError("checksum");
}
// Do we have a meeting id? If none, complain.
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
errors.missingParamError("meetingID");
}
} else {
errors.missingParamError("meetingID");
}
String externalMeetingId = params.meetingID
if (errors.hasErrors()) {
respondWithErrors(errors)
return
}
// Do we agree on the checksum? If not, complain.
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
respondWithErrors(errors)
return
}
// Everything is good so far. Translate the external meeting id to an internal meeting id. If
// we can't find the meeting, complain.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
@ -680,101 +455,21 @@ class ApiController {
String API_CALL = "end"
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String validationResponse = validateRequest(
ValidationService.ApiCall.END,
request.getParameterMap(),
request.getQueryString()
)
// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
} else {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
if (StringUtils.isEmpty(params.password)) {
invalid("invalidPassword", "You must supply the moderator password for this call.");
return
}
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility
ApiErrors errors = new ApiErrors()
// Do we have a checksum? If none, complain.
if (StringUtils.isEmpty(params.checksum)) {
errors.missingParamError("checksum");
}
// Do we have a meeting id? If none, complain.
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
errors.missingParamError("meetingID");
}
} else {
errors.missingParamError("meetingID");
}
String externalMeetingId = params.meetingID
// Do we have a password? If not, complain.
String modPW = params.password
if (StringUtils.isEmpty(modPW)) {
errors.missingParamError("password");
}
if (errors.hasErrors()) {
respondWithErrors(errors)
return
}
// Do we agree on the checksum? If not, complain.
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
respondWithErrors(errors)
return
}
// Everything is good so far. Translate the external meeting id to an internal meeting id. If
// we can't find the meeting, complain.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId)
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId);
if (meeting == null) {
// BEGIN - backward compatibility
invalid("notFound", "We could not find a meeting with that meeting ID - perhaps the meeting is not yet running?");
return;
// END - backward compatibility
errors.invalidMeetingIdError();
respondWithErrors(errors)
return;
}
if (meeting.getModeratorPassword().equals(modPW) == false) {
// BEGIN - backward compatibility
invalid("invalidPassword", "You must supply the moderator password for this call.");
return;
// END - backward compatibility
errors.invalidPasswordError();
respondWithErrors(errors)
return;
}
Meeting meeting = meetingService.getMeeting(internalMeetingId)
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingid", meeting.getInternalId());
@ -807,79 +502,21 @@ class ApiController {
String API_CALL = "getMeetingInfo"
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String validationResponse = validateRequest(
ValidationService.ApiCall.GET_MEETING_INFO,
request.getParameterMap(),
request.getQueryString()
)
// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
} else {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility
ApiErrors errors = new ApiErrors()
// Do we have a checksum? If none, complain.
if (StringUtils.isEmpty(params.checksum)) {
errors.missingParamError("checksum");
}
// Do we have a meeting id? If none, complain.
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
errors.missingParamError("meetingID");
}
} else {
errors.missingParamError("meetingID");
}
String externalMeetingId = params.meetingID
if (errors.hasErrors()) {
respondWithErrors(errors)
return
}
// Do we agree on the checksum? If not, complain.
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
respondWithErrors(errors)
return
}
// Everything is good so far. Translate the external meeting id to an internal meeting id. If
// we can't find the meeting, complain.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
log.info("Retrieving meeting ${internalMeetingId}")
Meeting meeting = meetingService.getMeeting(internalMeetingId);
if (meeting == null) {
// BEGIN - backward compatibility
invalid("notFound", "We could not find a meeting with that meeting ID");
return;
// END - backward compatibility
errors.invalidMeetingIdError();
respondWithErrors(errors)
return;
}
withFormat {
xml {
@ -895,39 +532,14 @@ class ApiController {
String API_CALL = "getMeetings"
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String validationResponse = validateRequest(
ValidationService.ApiCall.GET_MEETINGS,
request.getParameterMap(),
request.getQueryString()
)
// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility
ApiErrors errors = new ApiErrors()
// Do we have a checksum? If none, complain.
if (StringUtils.isEmpty(params.checksum)) {
errors.missingParamError("checksum");
}
if (errors.hasErrors()) {
respondWithErrors(errors)
return
}
// Do we agree on the checksum? If not, complain.
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
respondWithErrors(errors)
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
@ -958,43 +570,18 @@ class ApiController {
String API_CALL = "getSessions"
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String validationResponse = validateRequest(
ValidationService.ApiCall.GET_SESSIONS,
request.getParameterMap(),
request.getQueryString()
)
// BEGIN - backward compatibility
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
invalid("checksumError", "You did not pass the checksum security check")
return
}
// END - backward compatibility
ApiErrors errors = new ApiErrors()
// Do we have a checksum? If none, complain.
if (StringUtils.isEmpty(params.checksum)) {
errors.missingParamError("checksum");
}
if (errors.hasErrors()) {
respondWithErrors(errors)
return
}
// Do we agree on the checksum? If not, complain.
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
errors.checksumError()
respondWithErrors(errors)
return
}
Collection<UserSession> sssns = meetingService.getSessions();
Collection<UserSession> sssns = meetingService.getSessions()
if (sssns == null || sssns.isEmpty()) {
response.addHeader("Cache-Control", "no-cache")
@ -1038,43 +625,23 @@ class ApiController {
String API_CALL = "setPollXML"
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
Map<String, String[]> reqParams = getParameters(request)
if (StringUtils.isEmpty(params.checksum)) {
invalid("checksumError", "You did not pass the checksum security check")
String validationResponse = validateRequest(
ValidationService.ApiCall.SET_POLL_XML,
reqParams,
request.getQueryString()
)
if(!validationResponse.isEmpty()) {
invalid("validationError", validationResponse)
return
}
if (StringUtils.isEmpty(params.pollXML)) {
invalid("configXMLError", "You did not pass a poll XML")
return
}
if (!StringUtils.isEmpty(params.meetingID)) {
params.meetingID = StringUtils.strip(params.meetingID);
if (StringUtils.isEmpty(params.meetingID)) {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
} else {
invalid("missingParamMeetingID", "You must specify a meeting ID for the meeting.");
return
}
// Translate the external meeting id into an internal meeting id.
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(params.meetingID);
Meeting meeting = meetingService.getMeeting(internalMeetingId);
if (meeting == null) {
// BEGIN - backward compatibility
invalid("invalidMeetingIdentifier", "The meeting ID that you supplied did not match any existing meetings");
return;
// END - backward compatibility
}
Map<String, String[]> reqParams = getParameters(request)
String pollXML = params.pollXML
@ -1087,37 +654,27 @@ class ApiController {
invalid("pollXMLError", "Cannot decode poll XML")
return;
}
def pollxml = new XmlSlurper().parseText(decodedPollXML);
if (!paramsProcessorUtil.isPostChecksumSame(API_CALL, reqParams)) {
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
invalid("pollXMLChecksumError", "pollXMLChecksumError: request did not pass the checksum security check.")
}
}
} else {
def pollxml = new XmlSlurper().parseText(decodedPollXML);
pollxml.children().each { poll ->
String title = poll.title.text();
String question = poll.question.text();
String questionType = poll.questionType.text();
pollxml.children().each { poll ->
String title = poll.title.text();
String question = poll.question.text();
String questionType = poll.questionType.text();
ArrayList<String> answers = new ArrayList<String>();
poll.answers.children().each { answer ->
answers.add(answer.text());
}
//send poll to BigBlueButton Apps
meetingService.createdPolls(meeting.getInternalId(), title, question, questionType, answers);
ArrayList<String> answers = new ArrayList<String>();
poll.answers.children().each { answer ->
answers.add(answer.text());
}
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
// No need to use the response builder here until we have a more complex response
render(text: "<response><returncode>$RESP_CODE_SUCCESS</returncode></response>", contentType: "text/xml")
}
//send poll to BigBlueButton Apps
meetingService.createdPolls(meeting.getInternalId(), title, question, questionType, answers);
}
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
// No need to use the response builder here until we have a more complex response
render(text: "<response><returncode>$RESP_CODE_SUCCESS</returncode></response>", contentType: "text/xml")
}
}
}
@ -1129,45 +686,20 @@ class ApiController {
String API_CALL = 'guestWait'
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
ApiErrors errors = new ApiErrors()
String msgKey = "defaultKey"
String msgValue = "defaultValue"
String destURL = paramsProcessorUtil.getDefaultLogoutUrl()
// Do we have a sessionToken? If none, complain.
String sessionToken = sanitizeSessionToken(params.sessionToken)
if (sessionToken == null) {
msgKey = "missingToken"
msgValue = "Guest missing session token."
respondWithJSONError(msgKey, msgValue, destURL)
return
}
String validationResponse = validateRequest(
ValidationService.ApiCall.GUEST_WAIT,
request.getParameterMap(),
request.getQueryString()
)
UserSession us = getUserSession(sessionToken)
if (us == null) {
msgKey = "missingSession"
msgValue = "Guest missing session."
respondWithJSONError(msgKey, msgValue, destURL)
return
}
Meeting meeting = meetingService.getMeeting(us.meetingID)
if (meeting == null) {
msgKey = "missingMeeting"
msgValue = "Meeting does not exist."
respondWithJSONError(msgKey, msgValue, destURL)
return
}
// Is this user joining a meeting that has been ended. If so, complain.
if (meeting.isForciblyEnded()) {
msgKey = "meetingEnded"
msgValue = "Meeting ended."
if(!validationResponse.isEmpty()) {
msgKey = "validationError"
msgValue = validationResponse
respondWithJSONError(msgKey, msgValue, destURL)
return
}
@ -1253,47 +785,34 @@ class ApiController {
String API_CALL = 'enter'
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String respMessage = "Session not found."
boolean reject = false;
String sessionToken = sanitizeSessionToken(params.sessionToken)
UserSession us = getUserSession(sessionToken);
Meeting meeting = null;
String validationResponse = validateRequest(
ValidationService.ApiCall.ENTER,
request.getParameterMap(),
request.getQueryString()
)
String respMessage = "Session not found."
if(!validationResponse.isEmpty()) {
respMessage = validationResponse
reject = true
}
String sessionToken = sanitizeSessionToken(params.sessionToken)
UserSession us = getUserSession(sessionToken)
Meeting meeting = meetingService.getMeeting(us.meetingID)
meeting.userEntered(us.internalUserId)
if (!hasValidSession(sessionToken)) {
reject = true;
} else {
meeting = meetingService.getMeeting(us.meetingID);
if (meeting == null || meeting.isForciblyEnded()) {
reject = true
respMessage = "Meeting not found or ended for session."
} else {
if (hasReachedMaxParticipants(meeting, us)) {
reject = true;
respMessage = "The number of participants allowed for this meeting has been reached.";
} else {
meeting.userEntered(us.internalUserId);
}
}
if (us.guestStatus.equals(GuestPolicy.DENY)) {
respMessage = "User denied for user with session."
reject = true
}
}
if (reject) {
// Determine the logout url so we can send the user there.
String logoutUrl = paramsProcessorUtil.getDefaultLogoutUrl()
if (us != null) {
logoutUrl = us.logoutUrl
}
logoutUrl = us.logoutUrl
response.addHeader("Cache-Control", "no-cache")
withFormat {
@ -1404,24 +923,24 @@ class ApiController {
String API_CALL = 'stuns'
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
boolean reject = false;
String validationResponse = validateRequest(
ValidationService.ApiCall.STUNS,
request.getParameterMap(),
request.getQueryString()
)
if(!validationResponse.isEmpty()) {
reject = true
}
String sessionToken = sanitizeSessionToken(params.sessionToken)
UserSession us = getUserSession(sessionToken);
Meeting meeting = null;
UserSession us = getUserSession(sessionToken)
Meeting meeting = meetingService.getMeeting(us.meetingID)
if (!hasValidSession(sessionToken)) {
reject = true;
} else {
meeting = meetingService.getMeeting(us.meetingID);
if (meeting == null || meeting.isForciblyEnded()) {
reject = true
}
}
if (reject) {
@ -1483,38 +1002,32 @@ class ApiController {
String API_CALL = 'signOut'
log.debug CONTROLLER_NAME + "#${API_CALL}"
//sanitizeInput
params.each {
key, value -> params[key] = sanitizeInput(value)
}
String validationResponse = validateRequest(
ValidationService.ApiCall.SIGN_OUT,
request.getParameterMap(),
request.getQueryString()
)
String sessionToken = sanitizeSessionToken(params.sessionToken)
Meeting meeting = null;
if (sessionToken != null) {
UserSession us = meetingService.removeUserSessionWithAuthToken(sessionToken);
if (us != null) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingid", us.meetingID);
logData.put("extMeetingid", us.externMeetingID);
logData.put("name", us.fullname);
logData.put("userid", us.internalUserId);
logData.put("sessionToken", sessionToken);
logData.put("message", "handle_signout_api");
logData.put("logCode", "signout_api");
logData.put("description", "Handling SIGNOUT API.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info(" --analytics-- data=" + logStr);
} else {
log.info("Could not find user session for session token {}", sessionToken)
}
if(validationResponse.isEmpty()) {
String sessionToken = sanitizeSessionToken(params.sessionToken)
UserSession us = meetingService.removeUserSessionWithAuthToken(sessionToken)
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingid", us.meetingID);
logData.put("extMeetingid", us.externMeetingID);
logData.put("name", us.fullname);
logData.put("userid", us.internalUserId);
logData.put("sessionToken", sessionToken);
logData.put("message", "handle_signout_api");
logData.put("logCode", "signout_api");
logData.put("description", "Handling SIGNOUT API.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info(" --analytics-- data=" + logStr)
session.removeAttribute(sessionToken)
} else {
log.info("Could not find user session for session token {}", params.sessionToken)
}
response.addHeader("Cache-Control", "no-cache")
@ -1726,7 +1239,7 @@ class ApiController {
return us
}
// Can be removed. Input sanitization is performed in the ValidatorService.
// Can be removed. Input sanitization is performed in the ValidationService.
private def sanitizeInput (input) {
if(input == null)
return
@ -1907,4 +1420,18 @@ class ApiController {
redirect(url: newUri)
}
private String validateRequest(ValidationService.ApiCall apiCall, Map<String, String[]> params, String queryString) {
Set<String> violations = validationService.validate(apiCall, params, queryString)
StringBuilder violationMessages = new StringBuilder()
if(!violations.isEmpty()) {
for(String violation: violations) {
violationMessages.append(violation + "\n");
log.error violation
}
}
return violationMessages.toString()
}
}