diff options
author | Joas Schilling <coding@schilljs.com> | 2017-10-11 18:23:32 +0300 |
---|---|---|
committer | Joachim Bauch <bauch@struktur.de> | 2017-11-02 13:23:51 +0300 |
commit | f7f9e7929c7c277a388b4ca358b3ef6bbed172f1 (patch) | |
tree | 3f491143205d97af7453ece5698e7edf2727f293 | |
parent | faf4e9c43ce6a3dc1ca720b667af5e41c7340447 (diff) |
Allow multiple signaling servers
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r-- | css/settings-admin.scss | 1 | ||||
-rw-r--r-- | js/admin/signaling-server.js | 136 | ||||
-rw-r--r-- | lib/BackendNotifier.php | 11 | ||||
-rw-r--r-- | lib/Config.php | 43 | ||||
-rw-r--r-- | lib/Controller/PageController.php | 8 | ||||
-rw-r--r-- | lib/Controller/SignalingController.php | 3 | ||||
-rw-r--r-- | lib/Settings/Admin/SignalingServer.php | 4 | ||||
-rw-r--r-- | templates/settings/admin/signaling-server.php | 18 |
8 files changed, 166 insertions, 58 deletions
diff --git a/css/settings-admin.scss b/css/settings-admin.scss index 1c7a0ba60..051e7d37f 100644 --- a/css/settings-admin.scss +++ b/css/settings-admin.scss @@ -6,6 +6,7 @@ .icon-delete, .icon-checkmark-color, div.stun-server:last-child .icon-add, + div.signaling-server:last-child .icon-add, div.turn-server:last-child .icon-add { display: inline-block; } diff --git a/js/admin/signaling-server.js b/js/admin/signaling-server.js index d3e67a3ae..de066ee0d 100644 --- a/js/admin/signaling-server.js +++ b/js/admin/signaling-server.js @@ -1,28 +1,127 @@ /* global OC, OCP, OCA, $, _, Handlebars */ -(function(OC, OCP, OCA, $) { +(function(OC, OCP, OCA, $, _, Handlebars) { 'use strict'; OCA.VideoCalls = OCA.VideoCalls || {}; OCA.VideoCalls.Admin = OCA.VideoCalls.Admin || {}; OCA.VideoCalls.Admin.SignalingServer = { - $signaling: undefined, + TEMPLATE: '<div class="signaling-server">' + + ' <input type="text" class="server" placeholder="wss://signaling.example.org" value="{{server}}">' + + ' <input type="checkbox" id="verify{{seed}}" name="verify{{seed}}" class="checkbox verify" value="1" {{#if verify}} checked="checked"{{/if}}>' + + ' <label for="verify{{seed}}">' + t('spreed', 'Validate SSL certificate') + '</label>' + + ' <a class="icon icon-delete" title="' + t('spreed', 'Delete server') + '"></a>' + + ' <a class="icon icon-add" title="' + t('spreed', 'Add new server') + '"></a>' + + ' <span class="icon icon-checkmark-color hidden" title="' + t('spreed', 'Saved') + '"></span>' + + '</div>', + $list: undefined, + $secret: undefined, + template: undefined, + seed: 0, init: function() { - this.$signaling = $('div.signaling-server'); - this.$signaling.find('input').on('change', this.saveServer); + this.template = Handlebars.compile(this.TEMPLATE); + this.$list = $('div.signaling-servers'); + this.$secret = $('#signaling_secret'); + this.renderList(); + + this.$secret.on('change', this.saveServers.bind(this)); + }, + + renderList: function() { + var data = this.$list.data('servers'); + + var hasServers = false; + if (!_.isUndefined(data.secret)) { + _.each(data.servers, function (server) { + this.$list.append( + this.renderServer(server) + ); + }.bind(this)); + + hasServers = data.servers.length !== 0; + + this.$secret.val(data.secret); + } + + if (!hasServers) { + this.addNewTemplate(); + } + + this.$secret.parents('p').first().removeClass('hidden'); + }, + + addNewTemplate: function() { + var $server = this.renderServer({ + validate: true + }); + this.$list.append($server); + return $server; + }, + + deleteServer: function(e) { + e.stopPropagation(); + + var $server = $(e.currentTarget).parents('div.signaling-server').first(); + $server.remove(); + + this.saveServers(); + + if (this.$list.find('div.signaling-server').length === 0) { + var $newServer = this.addNewTemplate(); + this.temporaryShowSuccess($newServer); + } }, - saveServer: function() { - // this.$signaling.find('input').removeClass('error'); - // this.$signaling.find('.icon-checkmark-color').addClass('hidden'); + saveServers: function() { + var servers = [], + $error = [], + $success = [], + self = this, + $secret = this.$secret, + secret = this.$secret.val().trim(); + + this.$list.find('input').removeClass('error'); + this.$secret.removeClass('error'); + this.$list.find('.icon-checkmark-color').addClass('hidden'); + + this.$list.find('div.signaling-server').each(function() { + var $row = $(this), + $server = $row.find('input.server'), + $verify = $row.find('input.verify'), + data = { + server: $server.val().trim(), + verify: $verify.val() === '1' + }; + + if (data.server === '') { + $error.push($server); + return; + } - // OCP.AppConfig.setValue('spreed', $(this).attr('name'), $(this).value, { - // success: function() { - // self.temporaryShowSuccess($server); - // } - // }); + if (secret === '') { + $error.push($secret); + return; + } + + $success.push($(this)); + servers.push(data); + }); + + OCP.AppConfig.setValue('spreed', 'signaling_servers', JSON.stringify({ + servers: servers, + secret: secret + }), { + success: function() { + _.each($error, function($input) { + $input.addClass('error'); + }); + _.each($success, function($server) { + self.temporaryShowSuccess($server); + }); + } + }); }, temporaryShowSuccess: function($server) { @@ -31,12 +130,23 @@ setTimeout(function() { $icon.addClass('hidden'); }, 2000); + }, + + renderServer: function(server) { + server.seed = this.seed++; + var $template = $(this.template(server)); + + $template.find('a.icon-add').on('click', this.addNewTemplate.bind(this)); + $template.find('a.icon-delete').on('click', this.deleteServer.bind(this)); + $template.find('input').on('change', this.saveServers.bind(this)); + + return $template; } }; -})(OC, OCP, OCA, $); +})(OC, OCP, OCA, $, _, Handlebars); $(document).ready(function(){ OCA.VideoCalls.Admin.SignalingServer.init(); diff --git a/lib/BackendNotifier.php b/lib/BackendNotifier.php index 14593de07..d91448897 100644 --- a/lib/BackendNotifier.php +++ b/lib/BackendNotifier.php @@ -74,11 +74,8 @@ class BackendNotifier{ } // We can use any server of the available backends. - $signaling = $signaling[array_rand($signaling)]; - if (substr($signaling, -1) === '/') { - $signaling = substr($signaling, 0, strlen($signaling) - 1); - } - $url = $signaling . $url; + $signaling['server'] = rtrim($signaling['server'], '/'); + $url = rtrim($signaling['server'], '/') . $url; if (substr($url, 0, 6) === 'wss://') { $url = 'https://' . substr($url, 6); } else if (substr($url, 0, 5) === 'ws://') { @@ -99,10 +96,10 @@ class BackendNotifier{ 'headers' => $headers, 'body' => $body, ]; - if ($this->config->allowInsecureSignaling()) { + if (!$signaling['verify']) { $params['verify'] = false; } - $response = $client->post($url, $params); + $client->post($url, $params); } /** diff --git a/lib/Config.php b/lib/Config.php index 5d5a72908..4bc04c79b 100644 --- a/lib/Config.php +++ b/lib/Config.php @@ -106,13 +106,19 @@ class Config { } /** - * @return string + * @return array */ public function getSignalingServer() { - $server = $this->config->getAppValue('spreed', 'signaling_server', ''); - if ($server === '') { + $config = $this->config->getAppValue('spreed', 'signaling_servers'); + $signaling = json_decode($config, true); + + if (!is_array($signaling)) { return []; } + + // For now we use a random server from the list + $server = $signaling['servers'][mt_rand(0, count($servers) - 1)]; + return explode('\n', $server); } @@ -120,15 +126,14 @@ class Config { * @return string */ public function getSignalingSecret() { - return $this->config->getAppValue('spreed', 'signaling_secret', ''); - } + $config = $this->config->getAppValue('spreed', 'signaling_servers'); + $signaling = json_decode($config, true); - /** - * @return bool - */ - public function allowInsecureSignaling() { - $skip_verify = $this->config->getAppValue('spreed', 'signaling_skip_verify_cert', ''); - return !empty($skip_verify); + if (!is_array($signaling)) { + return ''; + } + + return $signaling['secret']; } /** @@ -137,9 +142,9 @@ class Config { */ public function getSignalingTicket($userId) { if (empty($userId)) { - $secret = $this->config->getAppValue('spreed', 'signaling_ticket_secret', ''); + $secret = $this->config->getAppValue('spreed', 'signaling_ticket_secret'); } else { - $secret = $this->config->getUserValue($userId, 'spreed', 'signaling_ticket_secret', ''); + $secret = $this->config->getUserValue($userId, 'spreed', 'signaling_ticket_secret'); } if (empty($secret)) { // Create secret lazily on first access. @@ -168,24 +173,24 @@ class Config { */ public function validateSignalingTicket($userId, $ticket) { if (empty($userId)) { - $secret = $this->config->getAppValue('spreed', 'signaling_ticket_secret', ''); + $secret = $this->config->getAppValue('spreed', 'signaling_ticket_secret'); } else { - $secret = $this->config->getUserValue($userId, 'spreed', 'signaling_ticket_secret', ''); + $secret = $this->config->getUserValue($userId, 'spreed', 'signaling_ticket_secret'); } if (empty($secret)) { return false; } - $lastcolon = strrpos($ticket, ':'); - if ($lastcolon === false) { + $lastColon = strrpos($ticket, ':'); + if ($lastColon === false) { // Immediately reject invalid formats. return false; } // TODO(fancycode): Should we reject tickets that are too old? - $data = substr($ticket, 0, $lastcolon); + $data = substr($ticket, 0, $lastColon); $hash = hash_hmac('sha256', $data, $secret); - return hash_equals($hash, substr($ticket, $lastcolon + 1)); + return hash_equals($hash, substr($ticket, $lastColon + 1)); } } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 6663829b4..190cae082 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -116,8 +116,14 @@ class PageController extends Controller { ]; } } + + $signaling = $this->config->getSignalingServer(); + if (!empty($signaling)) { + $signaling = $signaling['server']; + } + return [ - 'server' => $this->config->getSignalingServer(), + 'server' => $signaling, 'ticket' => $this->config->getSignalingTicket($this->userId), 'stunservers' => $stun, 'turnservers' => $turn, diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php index ee6efc9e5..947f437e5 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -215,7 +215,7 @@ class SignalingController extends OCSController { return $usersInRoom; } - /* + /** * Check if the current request is coming from an allowed backend. * * The backends are sending the custom header "Spreed-Signaling-Random" @@ -249,7 +249,6 @@ class SignalingController extends OCSController { * @NoCSRFRequired * @PublicPage * - * @param string $message * @return JSONResponse */ public function backend() { diff --git a/lib/Settings/Admin/SignalingServer.php b/lib/Settings/Admin/SignalingServer.php index 739f9e55d..c5ad31d93 100644 --- a/lib/Settings/Admin/SignalingServer.php +++ b/lib/Settings/Admin/SignalingServer.php @@ -40,9 +40,7 @@ class SignalingServer implements ISettings { */ public function getForm() { $parameters = [ - 'signalingServer' => $this->config->getAppValue('spreed', 'signaling_server'), - 'signalingSecret' => $this->config->getAppValue('spreed', 'signaling_secret'), - 'signalingSkipVerifyCert' => $this->config->getAppValue('spreed', 'signaling_skip_verify_cert'), + 'signalingServers' => $this->config->getAppValue('spreed', 'signaling_servers'), ]; return new TemplateResponse('spreed', 'settings/admin/signaling-server', $parameters, ''); diff --git a/templates/settings/admin/signaling-server.php b/templates/settings/admin/signaling-server.php index b07e2a566..d078f93c2 100644 --- a/templates/settings/admin/signaling-server.php +++ b/templates/settings/admin/signaling-server.php @@ -9,21 +9,13 @@ style('spreed', ['settings-admin']); <h3><?php p($l->t('Signaling server')) ?></h3> <p class="settings-hint"><?php p($l->t('An external signaling server can optionally be used for larger installations. Leave empty to use the internal signaling server.')) ?></p> - <p> - <label for="signaling_server"><?php p($l->t('External signaling server')) ?></label> - <input type="text" id="signaling_server" - name="signaling_server" placeholder="wss://signaling.example.org" - value="<?php p($_['signalingServer']) ?>" /> - </p> - <p> - <input type="checkbox" id="signaling_skip_verify_cert" name="signaling_skip_verify_cert" class="checkbox" value="1" <?php p($_['signalingSkipVerifyCert'] ? 'checked="checked"' : '') ?>> - <label for="signaling_skip_verify_cert"><?php p($l->t('Allow invalid certificates when connecting to the external signaling server? Only enable this if required for development!')) ?> - </label> - </p> - <p> + <div class="signaling-servers" data-servers="<?php p($_['signalingServers']) ?>"> + </div> + + <p class="hidden"> <label for="signaling_secret"><?php p($l->t('Shared secret')) ?></label> <input type="text" id="signaling_secret" - name="signaling_secret" placeholder="shared secret" + name="signaling_secret" placeholder="<?php p($l->t('Shared secret')) ?>" value="<?php p($_['signalingSecret']) ?>" /> </p> </div> |