Includes a minimal authentication step before SFU connections
This commit is contained in:
parent
2223f3de38
commit
b5427737d7
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user