From 0875448ab9c4f80bffc38781f8d7f0535ab50fbb Mon Sep 17 00:00:00 2001 From: Klaus Weidenbach Date: Sun, 5 Oct 2014 14:51:58 +0200 Subject: Some documentation of include/auth.php. --- include/auth.php | 127 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/include/auth.php b/include/auth.php index f8188f443..8f68fc562 100644 --- a/include/auth.php +++ b/include/auth.php @@ -1,11 +1,23 @@ -module === 'logout')) { - + if(((x($_POST, 'auth-params')) && ($_POST['auth-params'] === 'logout')) || ($a->module === 'logout')) { // process logout request $args = array('channel_id' => local_user()); call_hooks('logging_out', $args); @@ -106,16 +119,16 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p // re-validate a visitor, optionally invoke "su" if permitted to do so - if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) { + if(x($_SESSION, 'visitor_id') && (! x($_SESSION, 'uid'))) { // if our authenticated guest is allowed to take control of the admin channel, make it so. - $admins = get_config('system','remote_admin'); - if($admins && is_array($admins) && in_array($_SESSION['visitor_id'],$admins)) { + $admins = get_config('system', 'remote_admin'); + if($admins && is_array($admins) && in_array($_SESSION['visitor_id'], $admins)) { $x = q("select * from account where account_email = '%s' and account_email != '' and ( account_flags & %d ) limit 1", - dbesc(get_config('system','admin_email')), + dbesc(get_config('system', 'admin_email')), intval(ACCOUNT_ROLE_ADMIN) ); if($x) { - new_cookie(60*60*24); // one day + new_cookie(60 * 60 * 24); // one day $_SESSION['last_login_date'] = datetime_convert(); unset($_SESSION['visitor_id']); // no longer a visitor authenticate_success($x[0], true, true); @@ -137,20 +150,19 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p // already logged in user returning - if(x($_SESSION,'uid') || x($_SESSION,'account_id')) { + if(x($_SESSION, 'uid') || x($_SESSION, 'account_id')) { // first check if we're enforcing that sessions can't change IP address - + // @todo what to do with IPv6 addresses if($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) { logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); - $partial1 = substr($_SESSION['addr'],0,strrpos($_SESSION['addr'],'.')); - $partial2 = substr($_SERVER['REMOTE_ADDR'],0,strrpos($_SERVER['REMOTE_ADDR'],'.')); + $partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.')); + $partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.')); - - $paranoia = intval(get_pconfig($_SESSION['uid'],'system','paranoia')); + $paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia')); if(! $paranoia) - $paranoia = intval(get_config('system','paranoia')); + $paranoia = intval(get_config('system', 'paranoia')); switch($paranoia) { case 0: @@ -158,8 +170,8 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p break; case 2: // check 2 octets - $partial1 = substr($partial1,0,strrpos($partial1,'.')); - $partial2 = substr($partial2,0,strrpos($partial2,'.')); + $partial1 = substr($partial1, 0, strrpos($partial1, '.')); + $partial2 = substr($partial2, 0, strrpos($partial2, '.')); if($partial1 == $partial2) break; case 1: @@ -169,12 +181,11 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p case 3: default: // check any difference at all - logger('Session address changed. Paranoid setting in effect, blocking session. ' + logger('Session address changed. Paranoid setting in effect, blocking session. ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']); nuke_session(); goaway(z_root()); break; - } } @@ -191,17 +202,15 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p if(strcmp(datetime_convert('UTC','UTC','now - 12 hours'), $_SESSION['last_login_date']) > 0 ) { $_SESSION['last_login_date'] = datetime_convert(); $login_refresh = true; - } - authenticate_success($r[0], false, false, false, $login_refresh); + } + authenticate_success($r[0], false, false, false, $login_refresh); } else { $_SESSION['account_id'] = 0; nuke_session(); goaway(z_root()); } - - } - + } // end logged in user returning } else { @@ -211,10 +220,10 @@ else { // handle a fresh login request - if((x($_POST,'password')) && strlen($_POST['password'])) - $encrypted = hash('whirlpool',trim($_POST['password'])); + if((x($_POST, 'password')) && strlen($_POST['password'])) + $encrypted = hash('whirlpool', trim($_POST['password'])); - if((x($_POST,'auth-params')) && $_POST['auth-params'] === 'login') { + if((x($_POST, 'auth-params')) && $_POST['auth-params'] === 'login') { $record = null; @@ -239,8 +248,7 @@ else { $record = $addon_auth['user_record']; } else { - - $record = get_app()->account = account_verify_password($_POST['username'],$_POST['password']); + $record = get_app()->account = account_verify_password($_POST['username'], $_POST['password']); if(get_app()->account) { $_SESSION['account_id'] = get_app()->account['account_id']; @@ -249,21 +257,20 @@ else { notice( t('Failed authentication') . EOL); } - logger('authenticate: ' . print_r(get_app()->account,true), LOGGER_DEBUG); - + logger('authenticate: ' . print_r(get_app()->account, true), LOGGER_DEBUG); } if((! $record) || (! count($record))) { $error = 'authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR']; logger($error); // Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention - $authlog = get_config('system', 'authlog'); - if ($authlog) - @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); + $authlog = get_config('system', 'authlog'); + if ($authlog) + @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); notice( t('Login failed.') . EOL ); goaway(z_root()); - } + } // If the user specified to remember the authentication, then change the cookie // to expire after one year (the default is when the browser is closed). @@ -293,11 +300,25 @@ else { } +/** + * @brief Returns the channel_id for a given openid_identity. + * + * Queries the values from pconfig configuration for the given openid_identity + * and returns the corresponding channel_id. + * + * @fixme How do we prevent that an OpenID identity is used more than once? + * + * @param string $authid + * The given openid_identity + * @return int|bool + * Return channel_id from pconfig or false. + */ function match_openid($authid) { - $r = q("select * from pconfig where cat = 'system' and k = 'openid' and v = '%s' limit 1", + // Query the uid/channel_id from pconfig for a given value. + $r = q("SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1", dbesc($authid) ); if($r) return $r[0]['uid']; return false; -} +} -- cgit v1.2.3 From 305498eb16e992ea1d0a94d52cc0f51c70cc7ae6 Mon Sep 17 00:00:00 2001 From: Klaus Weidenbach Date: Sun, 5 Oct 2014 15:24:16 +0200 Subject: Some documentation for include/reddav.php and a bit code cleanup. Removed some duplicate code in RedBasicAuth and restructured a bit. Still work in progress. --- include/reddav.php | 195 +++++++++++++++++++++++++++++++++++++++++------------ mod/cloud.php | 86 +++++++++++------------ 2 files changed, 197 insertions(+), 84 deletions(-) diff --git a/include/reddav.php b/include/reddav.php index c4ef5bd08..3de24661e 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -9,6 +9,8 @@ * You find the original SabreDAV classes under @ref vendor/sabre/dav/. * We need to use SabreDAV 1.8.x for PHP5.3 compatibility. SabreDAV >= 2.0 * requires PHP >= 5.4. + * + * @todo split up the classes into own files. */ use Sabre\DAV; @@ -25,6 +27,8 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { /** * @brief The path inside /cloud + * + * @var string */ private $red_path; private $folder_hash; @@ -32,6 +36,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { * @brief The full path as seen in the browser. * /cloud + $red_path * @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug + * @var string */ private $ext_path; private $root_dir = ''; @@ -39,6 +44,8 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { /** * @brief The real path on the filesystem. * The actual path in store/ with the hashed names. + * + * @var string */ private $os_path = ''; @@ -107,7 +114,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { throw new DAV\Exception\Forbidden('Permission denied.'); } - + if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { throw new DAV\Exception\Forbidden('Permission denied.'); } @@ -971,78 +978,111 @@ function RedFileData($file, &$auth, $test = false) { /** - * RedBasicAuth class. + * @brief Authentication backend class for RedDAV. + * + * This class also contains some data which is not necessary for authentication + * like timezone settings. * */ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { - // @fixme mod/cloud.php:61 - public $channel_name = ''; - // @fixme mod/cloud.php:62 + /** + * @brief This variable holds the currently logged-in channel_address. + * + * It is used for building path in filestorage/. + * + * @var string|null + */ + protected $channel_name = null; + /** + * channel_id of the current channel of the logged-in account. + * + * @var int + */ public $channel_id = 0; - // @fixme mod/cloud.php:63 + /** + * channel_hash of the current channel of the logged-in account. + * + * @var string + */ public $channel_hash = ''; - // @fixme mod/cloud.php:68 + /** + * Set in mod/cloud.php to observer_hash. + * + * @var string + */ public $observer = ''; - // @fixme include/reddav.php:51 + /** + * + * @see RedBrowser::set_writeable() + * @var DAV\Browser\Plugin + */ public $browser; - // @fixme include/reddav.php:92 - public $owner_id; - // @fixme include/reddav.php:283 + /** + * channel_id of the current visited path. Set in RedDirectory::getDir(). + * + * @var int + */ + public $owner_id = 0; + /** + * channel_name of the current visited path. Set in RedDirectory::getDir(). + * + * Used for creating the path in cloud/ + * + * @var string + */ public $owner_nick = ''; - // @fixme mod/cloud.php:66 - public $timezone; + /** + * Timezone from the visiting channel's channel_timezone. + * + * Used in @ref RedBrowser + * + * @var string + */ + protected $timezone = ''; + /** + * @brief Validates a username and password. + * + * Guest access is granted with the password "+++". * + * @see DAV\Auth\Backend\AbstractBasic::validateUserPass * @param string $username * @param string $password + * @return bool */ protected function validateUserPass($username, $password) { - if (trim($password) === '+++') { - logger('reddav: validateUserPass: guest ' . $username); + logger('(DAV): RedBasicAuth::validateUserPass(): guest ' . $username); return true; } require_once('include/auth.php'); $record = account_verify_password($username, $password); if ($record && $record['account_default_channel']) { - $r = q("select * from channel where channel_account_id = %d and channel_id = %d limit 1", + $r = q("SELECT * FROM channel WHERE channel_account_id = %d AND channel_id = %d LIMIT 1", intval($record['account_id']), intval($record['account_default_channel']) ); if ($r) { - $this->currentUser = $r[0]['channel_address']; - $this->channel_name = $r[0]['channel_address']; - $this->channel_id = $r[0]['channel_id']; - $this->channel_hash = $this->observer = $r[0]['channel_hash']; - $_SESSION['uid'] = $r[0]['channel_id']; - $_SESSION['account_id'] = $r[0]['channel_account_id']; - $_SESSION['authenticated'] = true; - return true; + return $this->setAuthenticated($r[0]); } } - $r = q("select * from channel where channel_address = '%s' limit 1", + $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", dbesc($username) ); if ($r) { - $x = q("select * from account where account_id = %d limit 1", + $x = q("SELECT account_flags, account_salt, account_password FROM account WHERE account_id = %d LIMIT 1", intval($r[0]['channel_account_id']) ); if ($x) { + // @fixme this foreach should not be needed? foreach ($x as $record) { if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED) && (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) { logger('(DAV) RedBasicAuth: password verified for ' . $username); - $this->currentUser = $r[0]['channel_address']; - $this->channel_name = $r[0]['channel_address']; - $this->channel_id = $r[0]['channel_id']; - $this->channel_hash = $this->observer = $r[0]['channel_hash']; - $_SESSION['uid'] = $r[0]['channel_id']; - $_SESSION['account_id'] = $r[0]['channel_account_id']; - $_SESSION['authenticated'] = true; - return true; + return $this->setAuthenticated($r[0]); } } } @@ -1051,12 +1091,68 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { return false; } + /** + * @brief Sets variables and session parameters after successfull authentication. + * + * @param array $r + * Array with the values for the authenticated channel. + * @return bool + */ + protected function setAuthenticated($r) { + $this->channel_name = $r['channel_address']; + $this->channel_id = $r['channel_id']; + $this->channel_hash = $this->observer = $r['channel_hash']; + $_SESSION['uid'] = $r['channel_id']; + $_SESSION['account_id'] = $r['channel_account_id']; + $_SESSION['authenticated'] = true; + return true; + } + + /** + * Sets the channel_name from the currently logged-in channel. + * + * @param string $name + * The channel's name + */ public function setCurrentUser($name) { - $this->currentUser = $name; + $this->channel_name = $name; + } + /** + * Returns information about the currently logged-in channel. + * + * If nobody is currently logged in, this method should return null. + * + * @see DAV\Auth\Backend\AbstractBasic::getCurrentUser + * @return string|null + */ + public function getCurrentUser() { + return $this->channel_name; } /** - * @brief Set browser plugin. + * @brief Sets the timezone from the channel in RedBasicAuth. + * + * Set in mod/cloud.php if the channel has a timezone set. + * + * @param string $timezone + * The channel's timezone. + * @return void + */ + public function setTimezone($timezone) { + $this->timezone = $timezone; + } + /** + * @brief Returns the timezone. + * + * @return string + * Return the channel's timezone. + */ + public function getTimezone() { + return $this->timezone; + } + + /** + * @brief Set browser plugin for SabreDAV. * * @see RedBrowser::set_writeable() * @param DAV\Browser\Plugin $browser @@ -1065,8 +1161,12 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { $this->browser = $browser; } - // internal? logging function - function log() { + /** + * Prints out all RedBasicAuth variables to logger(). + * + * @return void + */ + public function log() { logger('dav: auth: channel_name ' . $this->channel_name, LOGGER_DATA); logger('dav: auth: channel_id ' . $this->channel_id, LOGGER_DATA); logger('dav: auth: channel_hash ' . $this->channel_hash, LOGGER_DATA); @@ -1080,13 +1180,23 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { /** - * RedBrowser class. + * @brief RedBrowser class. * + * RedBrowser is a SabreDAV server-plugin to provide a view to the DAV in + * the browser */ class RedBrowser extends DAV\Browser\Plugin { + /** + * @var RedBasicAuth + */ private $auth; + /** + * @brief Constructor for RedBrowser. + * + * @param RedBasicAuth &$auth + */ function __construct(&$auth) { $this->auth = $auth; $this->enableAssets = false; @@ -1096,6 +1206,7 @@ class RedBrowser extends DAV\Browser\Plugin { // directory and who the owner and observer are. So we add a pointer to the browser into the auth module and vice // versa. Then when we've figured out what directory is actually being accessed, we call the following function // to decide whether or not to show web elements which include writeable objects. + // @todo Maybe this can be solved with some $server->subscribeEvent()? function set_writeable() { if (! $this->auth->owner_id) { $this->enablePost = false; @@ -1117,8 +1228,8 @@ class RedBrowser extends DAV\Browser\Plugin { // (owner_id = channel_id) is visitor owner of this directory? $is_owner = ((local_user() && $this->auth->owner_id == local_user()) ? true : false); - if ($this->auth->timezone) - date_default_timezone_set($this->auth->timezone); + if ($this->auth->getTimezone()) + date_default_timezone_set($this->auth->getTimezone()); require_once('include/conversation.php'); @@ -1237,7 +1348,7 @@ class RedBrowser extends DAV\Browser\Plugin { // put the array for this file together $ft['attachId'] = $this->findAttachIdByHash($attachHash); - $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->channel_name; + $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); $ft['icon'] = $icon; $ft['attachIcon'] = (($size) ? $attachIcon : ''); // @todo Should this be an item value, not a global one? diff --git a/mod/cloud.php b/mod/cloud.php index 1765c0b69..c21c4c4b6 100644 --- a/mod/cloud.php +++ b/mod/cloud.php @@ -1,31 +1,34 @@ 1) $which = argv(1); $profile = 0; - $channel = $a->get_channel(); - $a->page['htmlhead'] .= '' . "\r\n" ; + $a->page['htmlhead'] .= '' . "\r\n"; if($which) profile_load($a, $which, $profile); @@ -70,12 +70,11 @@ function cloud_init(&$a) { if(local_user()) { $channel = $a->get_channel(); $auth->setCurrentUser($channel['channel_address']); - $auth->channel_name = $channel['channel_address']; $auth->channel_id = $channel['channel_id']; $auth->channel_hash = $channel['channel_hash']; $auth->channel_account_id = $channel['channel_account_id']; if($channel['channel_timezone']) - $auth->timezone = $channel['channel_timezone']; + $auth->setTimezone($channel['channel_timezone']); } $auth->observer = $ob_hash; } @@ -83,13 +82,13 @@ function cloud_init(&$a) { if($_GET['davguest']) $_SESSION['davguest'] = true; - $_SERVER['QUERY_STRING'] = str_replace(array('?f=','&f='),array('',''),$_SERVER['QUERY_STRING']); + $_SERVER['QUERY_STRING'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['QUERY_STRING']); $_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']); - $_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism','',$_SERVER['QUERY_STRING']); + $_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['QUERY_STRING']); - $_SERVER['REQUEST_URI'] = str_replace(array('?f=','&f='),array('',''),$_SERVER['REQUEST_URI']); + $_SERVER['REQUEST_URI'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['REQUEST_URI']); $_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']); - $_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism','',$_SERVER['REQUEST_URI']); + $_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['REQUEST_URI']); $rootDirectory = new RedDirectory('/', $auth); @@ -101,11 +100,15 @@ function cloud_init(&$a) { $server->addPlugin($lockPlugin); - // The next section of code allows us to bypass prompting for http-auth if a FILE is being accessed anonymously and permissions - // allow this. This way one can create hotlinks to public media files in their cloud and anonymous viewers won't get asked to login. - // If a DIRECTORY is accessed or there are permission issues accessing the file and we aren't previously authenticated via zot, - // prompt for HTTP-auth. This will be the default case for mounting a DAV directory. - // In order to avoid prompting for passwords for viewing a DIRECTORY, add the URL query parameter 'davguest=1' + // The next section of code allows us to bypass prompting for http-auth if a + // FILE is being accessed anonymously and permissions allow this. This way + // one can create hotlinks to public media files in their cloud and anonymous + // viewers won't get asked to login. + // If a DIRECTORY is accessed or there are permission issues accessing the + // file and we aren't previously authenticated via zot, prompt for HTTP-auth. + // This will be the default case for mounting a DAV directory. + // In order to avoid prompting for passwords for viewing a DIRECTORY, add + // the URL query parameter 'davguest=1'. $isapublic_file = false; $davguest = ((x($_SESSION, 'davguest')) ? true : false); @@ -116,22 +119,21 @@ function cloud_init(&$a) { if($x instanceof RedFile) $isapublic_file = true; } - catch ( Exception $e ) { + catch (Exception $e) { $isapublic_file = false; } } if((! $auth->observer) && (! $isapublic_file) && (! $davguest)) { try { - $auth->Authenticate($server, t('Red Matrix - Guests: Username: {your email address}, Password: +++')); + $auth->Authenticate($server, t('RedMatrix - Guests: Username: {your email address}, Password: +++')); } - catch ( Exception $e) { + catch (Exception $e) { logger('mod_cloud: auth exception' . $e->getMessage()); http_status_exit($e->getHTTPCode(), $e->getMessage()); } } - // provide a directory view for the cloud in Red Matrix $browser = new RedBrowser($auth); -- cgit v1.2.3