/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2018 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include namespace { constexpr const char* KeyIdentifier = "Identifier"; constexpr const char* TcpSocketType = "TcpSocket"; constexpr const char* WebSocketType = "WebSocket"; constexpr const char* DenyAccess = "Deny"; constexpr const char* RequirePassword = "RequirePassword"; constexpr const char* AllowAccess = "Allow"; constexpr openspace::properties::Property::PropertyInfo EnabledInfo = { "Enabled", "Is Enabled", "This setting determines whether this server interface is enabled or not." }; constexpr openspace::properties::Property::PropertyInfo TypeInfo = { "Type", "Type", "Whether the interface is using a Socket or a WebSocket" }; constexpr openspace::properties::Property::PropertyInfo PortInfo = { "Port", "Port", "The network port to use for this sevrer interface" }; constexpr openspace::properties::Property::PropertyInfo DefaultAccessInfo = { "DefaultAccess", "Default Access", "Sets the default access policy: Allow, RequirePassword or Deny" }; constexpr openspace::properties::Property::PropertyInfo AllowAddressesInfo = { "AllowAddresses", "Allow Addresses", "Ip addresses or domains that should always be allowed access to this interface" }; constexpr openspace::properties::Property::PropertyInfo RequirePasswordAddressesInfo = { "RequirePasswordAddresses", "Require Password Addresses", "Ip addresses or domains that should be allowed access if they provide a password" }; constexpr openspace::properties::Property::PropertyInfo DenyAddressesInfo = { "DenyAddresses", "Deny Addresses", "Ip addresses or domains that should never be allowed access to this interface" }; constexpr openspace::properties::Property::PropertyInfo PasswordInfo = { "Password", "Password", "Password for connecting to this interface" }; } namespace openspace { std::unique_ptr ServerInterface::createFromDictionary( const ghoul::Dictionary& config) { // TODO: Use documentation to verify dictionary std::unique_ptr si = std::make_unique(config); return si; } ServerInterface::ServerInterface(const ghoul::Dictionary& config) : properties::PropertyOwner({ "", "", "" }) , _type(TypeInfo) , _port(PortInfo, 0) , _enabled(EnabledInfo) , _allowAddresses(AllowAddressesInfo) , _requirePasswordAddresses(RequirePasswordAddressesInfo) , _denyAddresses(DenyAddressesInfo) , _defaultAccess(DefaultAccessInfo) , _password(PasswordInfo) { _type.addOption(static_cast(InterfaceType::TcpSocket), TcpSocketType); _type.addOption(static_cast(InterfaceType::WebSocket), WebSocketType); _defaultAccess.addOption(static_cast(Access::Deny), DenyAccess); _defaultAccess.addOption(static_cast(Access::RequirePassword), RequirePassword); _defaultAccess.addOption(static_cast(Access::Allow), AllowAccess); const std::string identifier = config.value(KeyIdentifier); auto readList = [config](const std::string& key, properties::StringListProperty& list) { if (config.hasValue(key)) { const ghoul::Dictionary& dict = config.value(key); std::vector v; for (const std::string& k : dict.keys()) { v.push_back(dict.value(k)); } list.set(v); } }; readList(AllowAddressesInfo.identifier, _allowAddresses); readList(DenyAddressesInfo.identifier, _denyAddresses); readList(RequirePasswordAddressesInfo.identifier, _requirePasswordAddresses); this->setIdentifier(identifier); this->setGuiName(identifier); this->setDescription("Settings for server interface " + identifier); const std::string type = config.value(TypeInfo.identifier); if (type == TcpSocketType) { _type = static_cast(InterfaceType::TcpSocket); } else if (type == WebSocketType) { _type = static_cast(InterfaceType::WebSocket); } if (config.hasValue(PasswordInfo.identifier)) { _password = config.value(PasswordInfo.identifier); } _port = static_cast(config.value(PortInfo.identifier)); _enabled = config.value(EnabledInfo.identifier); std::function reinitialize = [this]() { deinitialize(); initialize(); }; _type.onChange(reinitialize); _port.onChange(reinitialize); _enabled.onChange(reinitialize); _defaultAccess.onChange(reinitialize); _allowAddresses.onChange(reinitialize); _requirePasswordAddresses.onChange(reinitialize); _denyAddresses.onChange(reinitialize); addProperty(_type); addProperty(_port); addProperty(_enabled); addProperty(_defaultAccess); addProperty(_allowAddresses); addProperty(_requirePasswordAddresses); addProperty(_denyAddresses); addProperty(_password); } ServerInterface::~ServerInterface() {} void ServerInterface::initialize() { if (!_enabled) { return; } switch (static_cast(_type.value())) { case InterfaceType::TcpSocket: _socketServer = std::make_unique(); break; case InterfaceType::WebSocket: _socketServer = std::make_unique(); break; } _socketServer->listen(_port); } void ServerInterface::deinitialize() { _socketServer->close(); } bool ServerInterface::isEnabled() const { return _enabled.value(); } bool ServerInterface::isActive() const { return _socketServer->isListening(); } int ServerInterface::port() const { return _port; } std::string ServerInterface::password() const { return _password; } bool ServerInterface::clientHasAccessWithoutPassword( const std::string& clientAddress) const { for (const std::string& address : _allowAddresses.value()) { if (clientAddress == address) { return true; } } Access access = static_cast(_defaultAccess.value()); if (access == Access::Allow) { for (const std::string& address : _denyAddresses.value()) { if (clientAddress == address) { return false; } } return true; } return false; } bool ServerInterface::clientIsBlocked(const std::string& clientAddress) const { for (const std::string& address : _denyAddresses.value()) { if (clientAddress == address) { return true; } } Access access = static_cast(_defaultAccess.value()); if (access == Access::Deny) { for (const std::string& address : _allowAddresses.value()) { if (clientAddress == address) { return false; } } for (const std::string& address : _requirePasswordAddresses.value()) { if (clientAddress == address) { return false; } } return true; } return false; } ghoul::io::SocketServer* ServerInterface::server() { return _socketServer.get(); } }