From 5c94eb846db6063d9de6b2e006386f72837f4821 Mon Sep 17 00:00:00 2001 From: Anton Georgiev Date: Thu, 27 May 2021 16:02:17 +0000 Subject: [PATCH] refactor: Isolate recording apis into RecordingController --- .../org/bigbluebutton/web/UrlMappings.groovy | 12 +- .../web/controllers/ApiController.groovy | 320 --------------- .../controllers/RecordingController.groovy | 368 +++++++++++++++++- 3 files changed, 377 insertions(+), 323 deletions(-) diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy index 0b91698310..97bf33b4ba 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy @@ -68,11 +68,11 @@ class UrlMappings { action = [GET: 'getSessionsHandler', POST: 'getSessionsHandler'] } - "/bigbluebutton/api/getRecordings"(controller: "api") { + "/bigbluebutton/api/getRecordings"(controller: "recording") { action = [GET: 'getRecordingsHandler', POST: 'getRecordingsHandler'] } - "/bigbluebutton/api/updateRecordings"(controller: "api") { + "/bigbluebutton/api/updateRecordings"(controller: "recording") { action = [GET: 'updateRecordingsHandler', POST: 'updateRecordingsHandler'] } @@ -92,6 +92,14 @@ class UrlMappings { action = [POST: 'putRecordingTextTrack'] } + "/bigbluebutton/api/publishRecordings"(controller: "recording") { + action = [GET: 'publishRecordings'] + } + + "/bigbluebutton/api/deleteRecordings"(controller: "recording") { + action = [GET: 'deleteRecordings'] + } + "/bigbluebutton/$controller/$action?/$id?(.${format})?" { constraints { // apply constraints here 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 1975ca49b4..bb98cea35b 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 @@ -1466,326 +1466,6 @@ class ApiController { } } - /****************************************************** - * GET_RECORDINGS API - ******************************************************/ - def getRecordingsHandler = { - String API_CALL = "getRecordings" - log.debug CONTROLLER_NAME + "#${API_CALL}" - - //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 - } - // END - backward compatibility - - ApiErrors errors = new ApiErrors() - - // Do we have a checksum? If none, complain. - if (StringUtils.isEmpty(params.checksum)) { - errors.missingParamError("checksum"); - respondWithErrors(errors) - return - } - - log.debug request.getQueryString() - - // Do we agree on the checksum? If not, complain. - if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { - errors.checksumError() - respondWithErrors(errors) - return - } - - List externalMeetingIds = new ArrayList(); - if (!StringUtils.isEmpty(params.meetingID)) { - externalMeetingIds = paramsProcessorUtil.decodeIds(params.meetingID); - } - - ArrayList internalRecordIds = new ArrayList() - if (!StringUtils.isEmpty(params.recordID)) { - internalRecordIds = paramsProcessorUtil.decodeIds(params.recordID) - } - - ArrayList states = new ArrayList() - if (!StringUtils.isEmpty(params.state)) { - states = paramsProcessorUtil.decodeIds(params.state) - } - - // Everything is good so far. - 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); - } - - for(String intRecId : internalRecordIds) { - log.debug intRecId - } - - Map metadataFilters = ParamsProcessorUtil.processMetaParam(params); - - def getRecordingsResult = meetingService.getRecordings2x(internalRecordIds, states, metadataFilters) - - withFormat { - xml { - render(text: getRecordingsResult, contentType: "text/xml") - } - } - } - - /****************************************************** - * PUBLISH_RECORDINGS API - ******************************************************/ - def publishRecordings = { - String API_CALL = "publishRecordings" - log.debug CONTROLLER_NAME + "#${API_CALL}" - - //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.recordID)) { - invalid("missingParamRecordID", "You must specify a recordID."); - return - } - - if (StringUtils.isEmpty(params.publish)) { - invalid("missingParamPublish", "You must specify a publish value true or false."); - 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"); - } - // Do we have a publish status? If none, complain. - String publish = params.publish - if (StringUtils.isEmpty(publish)) { - errors.missingParamError("publish"); - } - - 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 - } - - ArrayList recordIdList = new ArrayList(); - 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 - - } - - meetingService.setPublishRecording(recordIdList, publish.toBoolean()); - withFormat { - xml { - // No need to use the response builder here until we have a more complex response - render(text: "$RESP_CODE_SUCCESS$publish", contentType: "text/xml") - } - } - } - - /****************************************************** - * DELETE_RECORDINGS API - ******************************************************/ - def deleteRecordings = { - String API_CALL = "deleteRecordings" - log.debug CONTROLLER_NAME + "#${API_CALL}" - - //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.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 recordIdList = new ArrayList(); - 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 - } - - meetingService.deleteRecordings(recordIdList); - withFormat { - xml { - // No need to use the response builder here until we have a more complex response - render(text: "$RESP_CODE_SUCCESStrue", contentType: "text/xml") - } - } - } - - /****************************************************** - * UPDATE_RECORDINGS API - ******************************************************/ - def updateRecordingsHandler = { - String API_CALL = "updateRecordings" - log.debug CONTROLLER_NAME + "#${API_CALL}" - - //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.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 recordIdList = new ArrayList(); - 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 metaParams = ParamsProcessorUtil.processMetaParam(params) - if (!metaParams.empty) { - //Proceed with the update - meetingService.updateRecordings(recordIdList, metaParams); - } - withFormat { - xml { - // No need to use the response builder here until we have a more complex response - render(text: "$RESP_CODE_SUCCESStrue", contentType: "text/xml") - } - } - } - def uploadDocuments(conf) { // log.debug("ApiController#uploadDocuments(${conf.getInternalId()})"); diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/RecordingController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/RecordingController.groovy index c07f145d83..ebf1dd4ce4 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/RecordingController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/RecordingController.groovy @@ -34,6 +34,328 @@ class RecordingController { } } + /****************************************************** + * GET_RECORDINGS API + ******************************************************/ + def getRecordingsHandler = { + String API_CALL = "getRecordings" + log.debug CONTROLLER_NAME + "#${API_CALL}" + + //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 + } + // END - backward compatibility + + ApiErrors errors = new ApiErrors() + + // Do we have a checksum? If none, complain. + if (StringUtils.isEmpty(params.checksum)) { + errors.missingParamError("checksum"); + respondWithErrors(errors) + return + } + + log.debug request.getQueryString() + + // Do we agree on the checksum? If not, complain. + if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { + errors.checksumError() + respondWithErrors(errors) + return + } + + List externalMeetingIds = new ArrayList(); + if (!StringUtils.isEmpty(params.meetingID)) { + externalMeetingIds = paramsProcessorUtil.decodeIds(params.meetingID); + } + + ArrayList internalRecordIds = new ArrayList() + if (!StringUtils.isEmpty(params.recordID)) { + internalRecordIds = paramsProcessorUtil.decodeIds(params.recordID) + } + + ArrayList states = new ArrayList() + if (!StringUtils.isEmpty(params.state)) { + states = paramsProcessorUtil.decodeIds(params.state) + } + + // Everything is good so far. + 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); + } + + for(String intRecId : internalRecordIds) { + log.debug intRecId + } + + Map metadataFilters = ParamsProcessorUtil.processMetaParam(params); + + def getRecordingsResult = meetingService.getRecordings2x(internalRecordIds, states, metadataFilters) + + withFormat { + xml { + render(text: getRecordingsResult, contentType: "text/xml") + } + } + } + + /****************************************************** + * UPDATE_RECORDINGS API + ******************************************************/ + def updateRecordingsHandler = { + String API_CALL = "updateRecordings" + log.debug CONTROLLER_NAME + "#${API_CALL}" + + //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.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 recordIdList = new ArrayList(); + 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 metaParams = ParamsProcessorUtil.processMetaParam(params) + if (!metaParams.empty) { + //Proceed with the update + meetingService.updateRecordings(recordIdList, metaParams); + } + withFormat { + xml { + // No need to use the response builder here until we have a more complex response + render(text: "$RESP_CODE_SUCCESStrue", contentType: "text/xml") + } + } + } + + + + /****************************************************** + * PUBLISH_RECORDINGS API + ******************************************************/ + def publishRecordings = { + String API_CALL = "publishRecordings" + log.debug CONTROLLER_NAME + "#${API_CALL}" + + //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.recordID)) { + invalid("missingParamRecordID", "You must specify a recordID."); + return + } + + if (StringUtils.isEmpty(params.publish)) { + invalid("missingParamPublish", "You must specify a publish value true or false."); + 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"); + } + // Do we have a publish status? If none, complain. + String publish = params.publish + if (StringUtils.isEmpty(publish)) { + errors.missingParamError("publish"); + } + + 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 + } + + ArrayList recordIdList = new ArrayList(); + 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 + + } + + meetingService.setPublishRecording(recordIdList, publish.toBoolean()); + withFormat { + xml { + // No need to use the response builder here until we have a more complex response + render(text: "$RESP_CODE_SUCCESS$publish", contentType: "text/xml") + } + } + } + + /****************************************************** + * DELETE_RECORDINGS API + ******************************************************/ + def deleteRecordings = { + String API_CALL = "deleteRecordings" + log.debug CONTROLLER_NAME + "#${API_CALL}" + + //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.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 recordIdList = new ArrayList(); + 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 + } + + meetingService.deleteRecordings(recordIdList); + withFormat { + xml { + // No need to use the response builder here until we have a more complex response + render(text: "$RESP_CODE_SUCCESStrue", contentType: "text/xml") + } + } + } + def checkTextTrackAuthToken = { try { def textTrackToken = request.getHeader("x-textTrack-token") @@ -281,4 +603,48 @@ class RecordingController { redirect(url: newUri) } -} \ No newline at end of file + private def sanitizeInput (input) { + if(input == null) + return + + if(!("java.lang.String".equals(input.getClass().getName()))) + return input + + StringUtils.strip(input.replaceAll("\\p{Cntrl}", "")); + } + + private void respondWithErrors(errorList, redirectResponse = false) { + log.debug CONTROLLER_NAME + "#invalid" + if (redirectResponse) { + ArrayList errors = new ArrayList(); + errorList.getErrors().each { error -> + Map errorMap = new LinkedHashMap() + errorMap.put("key", error[0]) + errorMap.put("message", error[1]) + errors.add(errorMap) + } + + JSONArray errorsJSONArray = new JSONArray(errors); + log.debug errorsJSONArray + + respondWithRedirect(errorsJSONArray) + } else { + response.addHeader("Cache-Control", "no-cache") + withFormat { + xml { + render(text: responseBuilder.buildErrors(errorList.getErrors(), RESP_CODE_FAILED), contentType: "text/xml") + } + json { + log.debug "Rendering as json" + def builder = new JsonBuilder() + builder.response { + returncode RESP_CODE_FAILED + messageKey key + message msg + } + render(contentType: "application/json", text: builder.toPrettyString()) + } + } + } + } +}