diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index 7dfef5366601b2aafe8f9287a93e08bc14867719..35c30a062cc68467369290699e3c11b30196284f 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -66,8 +66,8 @@ class ApiController { private static final String CONTROLLER_NAME = 'ApiController' private static final String RESP_CODE_SUCCESS = 'SUCCESS' private static final String RESP_CODE_FAILED = 'FAILED' - private static final String SECURITY_SALT = '639259d4-9dd8-4b25-bf01-95f9567eaf4b' - private static final String API_VERSION = '0.81' + private static final String ROLE_MODERATOR = "MODERATOR"; + private static final String ROLE_ATTENDEE = "VIEWER"; private static final String REDIRECT_RESPONSE = true MeetingService meetingService; @@ -77,8 +77,6 @@ class ApiController { PresentationUrlDownloadService presDownloadService StunTurnService stunTurnService - - /* general methods */ def index = { log.debug CONTROLLER_NAME + "#index" @@ -95,24 +93,6 @@ class ApiController { } } - /*********************************** - * BREAKOUT TEST (API) - ***********************************/ - def breakout = { - if(!StringUtils.isEmpty(params.meetingId)) { - String meetingId = StringUtils.strip(params.meetingId); - println("MeetingId = " + meetingId) - } else { - println("Missing meetingId") - return - } - - if (StringUtils.isEmpty(params.password)) { - println("Missing password") - return - } - } - /*********************************** * CREATE (API) ***********************************/ @@ -287,8 +267,7 @@ class ApiController { if (StringUtils.isEmpty(params.meetingID)) { errors.missingParamError("meetingID"); } - } - else { + } else { errors.missingParamError("meetingID"); } String externalMeetingId = params.meetingID @@ -331,7 +310,7 @@ class ApiController { if (params.createTime != null) { long createTime = 0; try{ - createTime=Long.parseLong(params.createTime); + createTime = Long.parseLong(params.createTime); } catch(Exception e){ log.warn("could not parse createTime param"); createTime = -1; @@ -379,16 +358,12 @@ class ApiController { return; } - String webVoice = StringUtils.isEmpty(params.webVoiceConf) ? meeting.getTelVoice() : params.webVoiceConf - - boolean redirectImm = parseBoolean(params.redirectImmediately) - // We preprend "w_" to our internal meeting Id to indicate that this is a web user. // For users joining using the phone, we will prepend "v_" so it will be easier // to distinguish users who doesn't have a web client. (ralam june 12, 2017) String internalUserID = "w_" + RandomStringUtils.randomAlphanumeric(12).toLowerCase() - String authToken = RandomStringUtils.randomAlphanumeric(12).toLowerCase() + String authToken = RandomStringUtils.randomAlphanumeric(12).toLowerCase() String sessionToken = RandomStringUtils.randomAlphanumeric(16).toLowerCase() @@ -475,14 +450,14 @@ class ApiController { // when maxUsers is set to 0, the validation is ignored int maxUsers = meeting.getMaxUsers(); if (maxUsers > 0 && meeting.getRegisteredUsers().size() >= maxUsers) { - // BEGIN - backward compatibility - invalid("maxParticipantsReached","The number of participants allowed for this meeting has been reached.", REDIRECT_RESPONSE); - return - // END - backward compatibility + // BEGIN - backward compatibility + invalid("maxParticipantsReached", "The number of participants allowed for this meeting has been reached.", REDIRECT_RESPONSE); + return + // END - backward compatibility - errors.maxParticipantsReached(); - respondWithErrors(errors, REDIRECT_RESPONSE); - return; + errors.maxParticipantsReached(); + respondWithErrors(errors, REDIRECT_RESPONSE); + return; } //Identify which of these to logs should be used. sessionToken or user-token @@ -1194,47 +1169,46 @@ class ApiController { } } - def getDefaultConfigXML = { - - String API_CALL = "getDefaultConfigXML" - ApiErrors errors = new ApiErrors(); + def getDefaultConfigXML = { - // 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 + String API_CALL = "getDefaultConfigXML" + ApiErrors errors = new ApiErrors(); + // BEGIN - backward compatibility + if (StringUtils.isEmpty(params.checksum)) { + invalid("checksumError", "You did not pass the checksum security check") + return + } - // Do we agree on the checksum? If not, complain. - if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { - errors.checksumError() - respondWithErrors(errors) - return - } + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + invalid("checksumError", "You did not pass the checksum security check") + return + } + // END - backward compatibility - String defConfigXML = paramsProcessorUtil.getDefaultConfigXML(); - if (StringUtils.isEmpty(defConfigXML)) { - // BEGIN - backward compatibility - invalid("noConfigFound","We could not find a config for this request.", REDIRECT_RESPONSE); - return - // END - backward compatibility + // Do we agree on the checksum? If not, complain. + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + errors.checksumError() + respondWithErrors(errors) + return + } - errors.noConfigFound(); - respondWithErrors(errors); - } + String defConfigXML = paramsProcessorUtil.getDefaultConfigXML(); + if (StringUtils.isEmpty(defConfigXML)) { + // BEGIN - backward compatibility + invalid("noConfigFound","We could not find a config for this request.", REDIRECT_RESPONSE); + return + // END - backward compatibility - response.addHeader("Cache-Control", "no-cache") - render text: defConfigXML, contentType: 'text/xml' + errors.noConfigFound(); + respondWithErrors(errors); } + response.addHeader("Cache-Control", "no-cache") + render text: defConfigXML, contentType: 'text/xml' + } + def configXML = { String API_CALL = 'configXML' log.debug CONTROLLER_NAME + "#${API_CALL}" @@ -1251,7 +1225,7 @@ class ApiController { sessionToken = StringUtils.strip(params.sessionToken) log.info("Getting ConfigXml for SessionToken = " + sessionToken) if (!session[sessionToken]) { - reject = true + reject = true } else { us = meetingService.getUserSessionWithAuthToken(sessionToken); if (us == null) reject = true @@ -1648,7 +1622,6 @@ class ApiController { } } - /************************************************* * SIGNOUT API *************************************************/ @@ -1707,7 +1680,7 @@ class ApiController { return } - log.debug request.getQueryString() + log.debug request.getQueryString() // Do we agree on the checksum? If not, complain. if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { @@ -1718,7 +1691,7 @@ class ApiController { List<String> externalMeetingIds = new ArrayList<String>(); if (!StringUtils.isEmpty(params.meetingID)) { - externalMeetingIds=paramsProcessorUtil.decodeIds(params.meetingID); + externalMeetingIds = paramsProcessorUtil.decodeIds(params.meetingID); } ArrayList<String> internalRecordIds = new ArrayList<String>() @@ -1732,7 +1705,7 @@ class ApiController { } // Everything is good so far. - if ( internalRecordIds.size() == 0 && externalMeetingIds.size() > 0 ) { + if (internalRecordIds.size() == 0 && externalMeetingIds.size() > 0) { // No recordIDs, process the request based on meetingID(s) // Translate the external meeting ids to internal meeting ids (which is the seed for the recordIDs). internalRecordIds = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingIds); @@ -1776,7 +1749,7 @@ class ApiController { return } - if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { invalid("checksumError", "You did not pass the checksum security check") return } @@ -1806,7 +1779,7 @@ class ApiController { } // Do we agree on the checksum? If not, complain. - if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { errors.checksumError() respondWithErrors(errors) return @@ -1814,7 +1787,7 @@ class ApiController { ArrayList<String> recordIdList = new ArrayList<String>(); if (!StringUtils.isEmpty(recordId)) { - recordIdList=paramsProcessorUtil.decodeIds(recordId); + recordIdList = paramsProcessorUtil.decodeIds(recordId); } if (!meetingService.existsAnyRecording(recordIdList)) { @@ -1856,7 +1829,7 @@ class ApiController { return } - if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { invalid("checksumError", "You did not pass the checksum security check") return } @@ -1881,7 +1854,7 @@ class ApiController { } // Do we agree on the checksum? If not, complain. - if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { errors.checksumError() respondWithErrors(errors) return @@ -1889,7 +1862,7 @@ class ApiController { List<String> recordIdList = new ArrayList<String>(); if (!StringUtils.isEmpty(recordId)) { - recordIdList=paramsProcessorUtil.decodeIds(recordId); + recordIdList = paramsProcessorUtil.decodeIds(recordId); } if (!meetingService.existsAnyRecording(recordIdList)) { @@ -1915,81 +1888,81 @@ class ApiController { /****************************************************** * UPDATE_RECORDINGS API ******************************************************/ - def updateRecordingsHandler = { - String API_CALL = "updateRecordings" - log.debug CONTROLLER_NAME + "#${API_CALL}" - - // BEGIN - backward compatibility - if (StringUtils.isEmpty(params.checksum)) { - invalid("checksumError", "You did not pass the checksum security check") - return - } - - if (StringUtils.isEmpty(params.recordID)) { - invalid("missingParamRecordID", "You must specify a recordID."); - 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 recording id? If none, complain. - String recordId = params.recordID - if (StringUtils.isEmpty(recordId)) { - errors.missingParamError("recordID"); - } - - 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 - } - - List<String> recordIdList = new ArrayList<String>(); - if (!StringUtils.isEmpty(recordId)) { - recordIdList=paramsProcessorUtil.decodeIds(recordId); - } - - if (!meetingService.existsAnyRecording(recordIdList)) { - // BEGIN - backward compatibility - invalid("notFound", "We could not find recordings"); - return; - // END - backward compatibility - } - - //Execute code specific for this call - Map<String, String> metaParams = ParamsProcessorUtil.processMetaParam(params) - if ( !metaParams.empty ) { - //Proceed with the update - meetingService.updateRecordings(recordIdList, metaParams); - } - withFormat { - xml { - render(contentType:"text/xml") { - response() { - returncode(RESP_CODE_SUCCESS) - updated(true) - } - } - } - } - } + def updateRecordingsHandler = { + String API_CALL = "updateRecordings" + log.debug CONTROLLER_NAME + "#${API_CALL}" + + // BEGIN - backward compatibility + if (StringUtils.isEmpty(params.checksum)) { + invalid("checksumError", "You did not pass the checksum security check") + return + } + + if (StringUtils.isEmpty(params.recordID)) { + invalid("missingParamRecordID", "You must specify a recordID."); + 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 recording id? If none, complain. + String recordId = params.recordID + if (StringUtils.isEmpty(recordId)) { + errors.missingParamError("recordID"); + } + + 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 + } + + List<String> recordIdList = new ArrayList<String>(); + if (!StringUtils.isEmpty(recordId)) { + recordIdList = paramsProcessorUtil.decodeIds(recordId); + } + + if (!meetingService.existsAnyRecording(recordIdList)) { + // BEGIN - backward compatibility + invalid("notFound", "We could not find recordings"); + return; + // END - backward compatibility + } + + //Execute code specific for this call + Map<String, String> metaParams = ParamsProcessorUtil.processMetaParam(params) + if (!metaParams.empty) { + //Proceed with the update + meetingService.updateRecordings(recordIdList, metaParams); + } + withFormat { + xml { + render(contentType: "text/xml") { + response() { + returncode(RESP_CODE_SUCCESS) + updated(true) + } + } + } + } + } def uploadDocuments(conf) { // log.debug("ApiController#uploadDocuments(${conf.getInternalId()})"); @@ -1999,7 +1972,7 @@ class ApiController { if (requestBody == null) { downloadAndProcessDocument(presentationService.defaultUploadedPresentation, conf.getInternalId(), - true /* default presentation */ ); + true /* default presentation */); } else { log.debug "Request body: \n" + requestBody; def xml = new XmlSlurper().parseText(requestBody); @@ -2015,7 +1988,7 @@ class ApiController { def b64 = new Base64() def decodedBytes = b64.decode(document.text().getBytes()) processDocumentFromRawBytes(decodedBytes, document.@name.toString(), - conf.getInternalId(), true /* default presentation */); + conf.getInternalId(), true /* default presentation */); } else { log.debug("presentation module config found, but it did not contain url or name attributes"); } @@ -2104,9 +2077,9 @@ class ApiController { meetingID() { mkp.yield(meeting.getExternalId()) } internalMeetingID(meeting.getInternalId()) if (meeting.isBreakout()) { - parentMeetingID() { mkp.yield(meeting.getParentMeetingId()) } - sequence() { mkp.yield(meeting.getSequence()) } - freeJoin() { mkp.yield(meeting.isFreeJoin()) } + parentMeetingID() { mkp.yield(meeting.getParentMeetingId()) } + sequence() { mkp.yield(meeting.getSequence()) } + freeJoin() { mkp.yield(meeting.isFreeJoin()) } } createTime(meeting.getCreateTime()) createDate(formatPrettyDate(meeting.getCreateTime())) @@ -2181,7 +2154,7 @@ class ApiController { moderatorPW() { mkp.yield(meeting.getModeratorPassword()) } createTime(meeting.getCreateTime()) voiceBridge() { mkp.yield(meeting.getTelVoice()) } - dialNumber() { mkp.yield(meeting.getDialNumber()) } + dialNumber() { mkp.yield(meeting.getDialNumber()) } createDate(formatPrettyDate(meeting.getCreateTime())) hasUserJoined(meeting.hasUserJoined()) duration(meeting.duration) @@ -2194,87 +2167,87 @@ class ApiController { } } - private void respondWithErrors(errorList, redirectResponse=false) { + private void respondWithErrors(errorList, redirectResponse = false) { log.debug CONTROLLER_NAME + "#invalid" if (redirectResponse) { - ArrayList<Object> errors = new ArrayList<Object>(); - errorList.getErrors().each { error -> - Map<String,String> errorMap = new LinkedHashMap<String,String>() - errorMap.put("key", error[0]) - errorMap.put("message", error[1]) - errors.add(errorMap) - } + ArrayList<Object> errors = new ArrayList<Object>(); + errorList.getErrors().each { error -> + Map<String, String> errorMap = new LinkedHashMap<String, String>() + errorMap.put("key", error[0]) + errorMap.put("message", error[1]) + errors.add(errorMap) + } - JSONArray errorsJSONArray = new JSONArray(errors); - log.debug errorsJSONArray + JSONArray errorsJSONArray = new JSONArray(errors); + log.debug errorsJSONArray - respondWithRedirect(errorsJSONArray) + respondWithRedirect(errorsJSONArray) } else { - response.addHeader("Cache-Control", "no-cache") - withFormat { - xml { - render(contentType:"text/xml") { - response() { - returncode(RESP_CODE_FAILED) - errors() { - ArrayList errs = errorList.getErrors(); - Iterator itr = errs.iterator(); - while (itr.hasNext()){ - String[] er = (String[]) itr.next(); - log.debug CONTROLLER_NAME + "#invalid" + er[0] - error(key: er[0], message: er[1]) - } + response.addHeader("Cache-Control", "no-cache") + withFormat { + xml { + render(contentType: "text/xml") { + response() { + returncode(RESP_CODE_FAILED) + errors() { + ArrayList errs = errorList.getErrors(); + Iterator itr = errs.iterator(); + while (itr.hasNext()) { + String[] er = (String[]) itr.next(); + log.debug CONTROLLER_NAME + "#invalid" + er[0] + error(key: er[0], message: er[1]) } } } } - json { - log.debug "Rendering as json" - render(contentType:"text/json") { - returncode(RESP_CODE_FAILED) - messageKey(key) - message(msg) - } + } + json { + log.debug "Rendering as json" + render(contentType: "text/json") { + returncode(RESP_CODE_FAILED) + messageKey(key) + message(msg) } } + } } } //TODO: method added for backward compatibility, it will be removed in next versions after 0.8 - private void invalid(key, msg, redirectResponse=false) { + private void invalid(key, msg, redirectResponse = false) { // Note: This xml scheme will be DEPRECATED. log.debug CONTROLLER_NAME + "#invalid " + msg if (redirectResponse) { - ArrayList<Object> errors = new ArrayList<Object>(); - Map<String,String> errorMap = new LinkedHashMap<String,String>() - errorMap.put("key", key) - errorMap.put("message", msg) - errors.add(errorMap) + ArrayList<Object> errors = new ArrayList<Object>(); + Map<String, String> errorMap = new LinkedHashMap<String, String>() + errorMap.put("key", key) + errorMap.put("message", msg) + errors.add(errorMap) - JSONArray errorsJSONArray = new JSONArray(errors); - log.debug errorsJSONArray + JSONArray errorsJSONArray = new JSONArray(errors); + log.debug errorsJSONArray - respondWithRedirect(errorsJSONArray) + respondWithRedirect(errorsJSONArray) } else { - response.addHeader("Cache-Control", "no-cache") - withFormat { - xml { - render(contentType:"text/xml") { - response() { - returncode(RESP_CODE_FAILED) - messageKey(key) - message(msg) - } - } - } - json { - log.debug "Rendering as json" - render(contentType:"text/json") { + response.addHeader("Cache-Control", "no-cache") + withFormat { + xml { + render(contentType: "text/xml") { + response() { returncode(RESP_CODE_FAILED) messageKey(key) message(msg) } } } + json { + log.debug "Rendering as json" + render(contentType: "text/json") { + returncode(RESP_CODE_FAILED) + messageKey(key) + message(msg) + } + } + } } } @@ -2283,19 +2256,19 @@ class ApiController { URI oldUri = URI.create(logoutUrl) if (!StringUtils.isEmpty(params.logoutURL)) { - try { - oldUri = URI.create(params.logoutURL) - } catch ( Exception e ) { - // Do nothing, the variable oldUri was already initialized - } + try { + oldUri = URI.create(params.logoutURL) + } catch (Exception e) { + // Do nothing, the variable oldUri was already initialized + } } String newQuery = oldUri.getQuery(); if (newQuery == null) { - newQuery = "errors=" + newQuery = "errors=" } else { - newQuery += "&" + "errors=" + newQuery += "&" + "errors=" } newQuery += errorsJSONArray