From ec937a5af1440b56c0c51bf4149324955b2ec469 Mon Sep 17 00:00:00 2001 From: Philipp Mundhenk Date: Sun, 14 Jan 2024 14:39:23 +0100 Subject: [PATCH] implemented ProxyAuth --- plugins/proxy-auth/LICENSE | 20 ++++ plugins/proxy-auth/index.php | 175 +++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 plugins/proxy-auth/LICENSE create mode 100644 plugins/proxy-auth/index.php diff --git a/plugins/proxy-auth/LICENSE b/plugins/proxy-auth/LICENSE new file mode 100644 index 000000000..172df18fd --- /dev/null +++ b/plugins/proxy-auth/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2023 Philipp Mundhenk + +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. diff --git a/plugins/proxy-auth/index.php b/plugins/proxy-auth/index.php new file mode 100644 index 000000000..1d8bbfe79 --- /dev/null +++ b/plugins/proxy-auth/index.php @@ -0,0 +1,175 @@ +addPartHook('ProxyAuth', 'ServiceProxyAuth'); + $this->addHook('login.credentials', 'MapEmailAddress'); + } + + /* by https://gist.github.com/tott/7684443 */ + /** + * Check if a given ip is in a network + * @param string $ip IP to check in IPV4 format eg. 127.0.0.1 + * @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed + * @return boolean true if the ip is in this range / false if not. + */ + private function ip_in_range( $ip, $range ) { + if ( strpos( $range, '/' ) == false ) { + $range .= '/32'; + } + // $range is in IP/CIDR format eg 127.0.0.1/24 + list( $range, $netmask ) = explode( '/', $range, 2 ); + $range_decimal = ip2long( $range ); + $ip_decimal = ip2long( $ip ); + $wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1; + $netmask_decimal = ~ $wildcard_decimal; + return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) ); + } + + public function MapEmailAddress(string &$sEmail, string &$sLogin, string &$sPassword) + { + $oActions = \RainLoop\Api::Actions(); + $oLogger = $oActions->Logger(); + $sPrefix = "ProxyAuthCREDENTIALS"; + $sLevel = LOG_DEBUG; + $sMsg = "sEmail= " . $sEmail; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + $sMasterUser = \trim($this->Config()->Get('plugin', 'master_user', '')); + $sMasterSeparator = \trim($this->Config()->Get('plugin', 'master_separator', '')); + + if (static::$login) { + $sEmail = str_replace($sMasterUser, "", $sEmail); + $sEmail = str_replace($sMasterSeparator, "", $sEmail); + } + } + + private static bool $login = false; + public function ServiceProxyAuth() : bool + { + $oActions = \RainLoop\Api::Actions(); + + $oException = null; + $oAccount = null; + + $oLogger = $oActions->Logger(); + $sLevel = LOG_DEBUG; + $sPrefix = "ProxyAuth"; + + $sMasterUser = \trim($this->Config()->Get('plugin', 'master_user', '')); + $sMasterSeparator = \trim($this->Config()->Get('plugin', 'master_separator', '')); + $sHeaderName = \trim($this->Config()->Get('plugin', 'header_name', '')); + + $sRemoteUser = $this->Manager()->Actions()->Http()->GetHeader($sHeaderName); + $sMsg = "Remote User: " . $sRemoteUser; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + $sProxyIP = $this->Config()->Get('plugin', 'proxy_ip', ''); + $sMsg = "ProxyIP: " . $sProxyIP; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + $sProxyCheck = $this->Config()->Get('plugin', 'proxy_check', ''); + $sClientIPs = $this->Manager()->Actions()->Http()->GetClientIP(true); + + if ($sProxyCheck) { + $sProxyRequest = false; + $sMsg = "checking client IPs: " . $sClientIPs; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + $sClientIPs = explode(", ", $sClientIPs); + if (is_array($sClientIPs)) { + foreach ($sClientIPs as &$sIP) { + $sMsg = "checking client IP: " . $sIP; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + if ($this->ip_in_range($sIP, $sProxyIP)) { + $sProxyRequest = true; + } + } + } else { + $sMsg = "checking client IP: " . $sClientIPs; + $oLogger->Write($sMsg, $sLevel, $sPrefix); + + if ($this->ip_in_range($sClientIPs, $sProxyIP)) { + $sProxyRequest = true; + } + } + } else { + $sProxyRequest = true; + } + + if ($sProxyRequest) { + $sEmail = $sRemoteUser . $sMasterSeparator . $sMasterUser; + $sPassword = \trim($this->Config()->Get('plugin', 'master_password', '')); + + try + { + static::$login = true; + $oAccount = $oActions->LoginProcess($sEmail, $sPassword); + if ($oAccount instanceof \RainLoop\Model\MainAccount) { + $oActions->SetAuthToken($oAccount); + } + } + catch (\Throwable $oException) + { + $oLogger = $oActions->Logger(); + $oLogger && $oLogger->WriteException($oException); + } + + \MailSo\Base\Http::Location('./'); + return true; + } + + \MailSo\Base\Http::Location('./'); + return true; + } + + protected function configMapping() : array + { + return array( + \RainLoop\Plugins\Property::NewInstance('master_separator') + ->SetLabel('Master User separator') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) + ->SetDescription('Sets the master user separator (format: )') + ->SetDefaultValue('*'), + \RainLoop\Plugins\Property::NewInstance('master_user') + ->SetLabel('Master User') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) + ->SetDescription('Username of master user') + ->SetDefaultValue('admin'), + \RainLoop\Plugins\Property::NewInstance('master_password') + ->SetLabel('Master Password') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) + ->SetDescription('Password for master user') + ->SetDefaultValue('adminpassword'), + \RainLoop\Plugins\Property::NewInstance('header_name') + ->SetLabel('Header Name') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) + ->SetDescription('Name of header containing username') + ->SetDefaultValue('Remote-User'), + \RainLoop\Plugins\Property::NewInstance('check_proxy') + ->SetLabel('Check Proxy') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDescription('Activates check if proxy is connecting') + ->SetDefaultValue(true), + \RainLoop\Plugins\Property::NewInstance('proxy_ip') + ->SetLabel('Proxy IPNet') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) + ->SetDescription('IP or Subnet of proxy, auth header will only be accepted from this address') + ->SetDefaultValue('10.1.0.0/24') + ); + } +}