Includes a minimal authentication step before SFU connections

This commit is contained in:
Pedro Beschorner Marin 2019-01-07 16:54:37 +00:00
parent 2223f3de38
commit b5427737d7
9 changed files with 110 additions and 6 deletions

View File

@ -1,10 +1,17 @@
package org.bigbluebutton.api.util;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
public class ParamsUtil {
private static Logger log = LoggerFactory.getLogger(ParamsUtil.class);
private static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9- ]*$");
public static final String INVALID_CHARS = ",";
@ -21,4 +28,23 @@ public class ParamsUtil {
public static boolean containsChar(String text, String chars) {
return StringUtils.containsAny(text, chars);
}
public static String getSessionToken(String url) {
String token = "undefined";
try {
String decodedURL = URLDecoder.decode(url, "UTF-8");
String[] splitedURL = decodedURL.split("\\?");
if (splitedURL.length == 2) {
String params = splitedURL[1];
for (String param : params.split("\\&")) {
if (param.startsWith("sessionToken=")) {
token = param.split("\\=")[1];
}
}
}
} catch (UnsupportedEncodingException e) {
log.error(e.toString());
}
return token;
}
}

View File

@ -55,7 +55,9 @@ Kurento = function (
this.kurentoPort = 'bbb-webrtc-sfu';
this.hostName = window.location.hostname;
this.socketUrl = `wss://${this.hostName}/${this.kurentoPort}`;
BBB.getSessionToken(sessionToken => {
this.socketUrl = `wss://${this.hostName}/${this.kurentoPort}?sessionToken=${sessionToken}`;
});
this.pingInterval = null;
if (chromeExtension != null) {

View File

@ -74,7 +74,7 @@ export default class KurentoAudioBridge extends BaseAudioBridge {
} finally {
logFunc('info', "iceServers", iceServers);
const options = {
wsUrl: SFU_URL,
wsUrl: Auth.authenticateURL(SFU_URL),
userName: this.user.name,
caleeName: `${GLOBAL_AUDIO_PREFIX}${this.voiceBridge}`,
iceServers,

View File

@ -59,7 +59,7 @@ export default class KurentoScreenshareBridge {
logger.error({ logCode: 'kurentowatchvideo_fetchstunturninfo_error' }, 'Screenshare bridge failed to fetch STUN/TURN info, using default');
} finally {
const options = {
wsUrl: SFU_URL,
wsUrl: Auth.authenticateURL(SFU_URL),
iceServers,
logger: modLogger,
};
@ -88,7 +88,7 @@ export default class KurentoScreenshareBridge {
logger.error({ logCode: 'kurentosharescreen_fetchstunturninfo_error' }, 'Screenshare bridge failed to fetch STUN/TURN info, using default');
} finally {
const options = {
wsUrl: SFU_URL,
wsUrl: Auth.authenticateURL(SFU_URL),
chromeExtension: CHROME_EXTENSION_KEY,
chromeScreenshareSources: CHROME_SCREENSHARE_SOURCES,
firefoxScreenshareSource: FIREFOX_SCREENSHARE_SOURCE,

View File

@ -8,6 +8,7 @@ import logger from '/imports/startup/client/logger';
import { Session } from 'meteor/session';
import browser from 'browser-detect';
import { tryGenerateIceCandidates } from '../../../utils/safari-webrtc';
import Auth from '/imports/ui/services/auth';
import VideoService from './service';
import VideoList from './video-list/component';
@ -110,7 +111,7 @@ class VideoProvider extends Component {
};
// Set a valid bbb-webrtc-sfu application server socket in the settings
this.ws = new ReconnectingWebSocket(Meteor.settings.public.kurento.wsUrl);
this.ws = new ReconnectingWebSocket(Auth.authenticateURL(Meteor.settings.public.kurento.wsUrl));
this.wsQueue = [];
this.visibility = new VisibilityEvent();

View File

@ -242,6 +242,18 @@ class Auth {
makeCall('validateAuthToken');
});
}
authenticateURL(url) {
let authURL = url;
if (authURL.indexOf('sessionToken=') === -1) {
if (authURL.indexOf('?') !== -1) {
authURL = authURL + '&sessionToken=' + this.sessionToken;
} else {
authURL= authURL + '?sessionToken=' + this.sessionToken;
}
}
return authURL;
}
}
const AuthSingleton = new Auth();

View File

@ -60,5 +60,17 @@
proxy_request_buffering off;
}
}
# To check connection authentication, include:
# auth_request /bigbluebutton/connection/checkAuthorization;
# auth_request_set $auth_status $upstream_status;
#
# and make sure to add sessionToken param in the request URI
location = /bigbluebutton/connection/checkAuthorization {
internal;
proxy_pass http://127.0.0.1:8080;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}

View File

@ -91,6 +91,10 @@ class UrlMappings {
action = [POST: 'putRecordingTextTrack']
}
"/connection/checkAuthorization"(controller:"connection") {
action = [GET:'checkAuthorization']
}
"/bigbluebutton/$controller/$action?/$id?(.${format})?" {
constraints {
// apply constraints here

View File

@ -0,0 +1,47 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2019 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.web.controllers
import org.bigbluebutton.api.MeetingService
import org.bigbluebutton.api.domain.UserSession
import org.bigbluebutton.api.util.ParamsUtil
class ConnectionController {
MeetingService meetingService
def checkAuthorization = {
try {
def uri = request.getHeader("x-original-uri")
def sessionToken = ParamsUtil.getSessionToken(uri)
UserSession userSession = meetingService.getUserSessionWithAuthToken(sessionToken)
response.addHeader("Cache-Control", "no-cache")
response.contentType = 'plain/text'
if (userSession != null && userSession.authed) {
response.setStatus(200)
response.outputStream << 'authorized'
} else {
response.setStatus(401)
response.outputStream << 'unauthorized'
}
} catch (IOException e) {
log.error("Error while authenticating connection.\n" + e.getMessage())
}
}
}