v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT Databased.php000064400000076012152101621640007131 0ustar00Return example: * * array( 'db' => 'MYSQL_DBNAME', * 'user' => 'MYSQL_USER', * 'prefix'=> 'MYSQL_PREFIX' * ) */ abstract class Am_Protect_Databased extends Am_Protect_Abstract implements Am_Protect_SingleLogin { const USER_NEED_SETPASS = 'user_need_setpass'; protected $_tableClass = "Am_Protect_Table"; /** @var Am_Protect_Table */ public $_table = null; /** @var DbSimple_Mysql */ public $_db; /** @var IntegrationTable */ public $_integrationTable; /** @var UserTable */ public $_userTable; /** @var Am_Protect_SessionTable */ public $_sessionTable; /** @var string table name without prefix */ protected $guessTablePattern = null; /** @var array of several fieldnames in the table */ protected $guessFieldsPattern = array( ); protected $groupMode = self::GROUP_NONE; const GROUP_NONE = 0; const GROUP_SINGLE = 1; const GROUP_MULTI = 2; public $sqlDebug = false; protected $skipAfterLogin = false; protected $skipCheckUniqLogin = false; /** @var array group => priority, must be built on first access */ protected $_priority = null; public function __construct(Am_Di $di, array $config) { parent::__construct($di, $config); if ($this->_tableClass === null) $this->_tableClass = get_class($this) . "_Table"; } /** @return IntegrationTable */ public function getIntegrationTable() { if (!$this->_integrationTable) $this->_integrationTable = $this->getDi()->integrationTable; return $this->_integrationTable; } /** @return UserTable */ public function getUserTable() { if (!$this->_userTable) $this->_userTable = $this->getDi()->userTable; return $this->_userTable; } public function _getTableClass() { return $this->_tableClass; } function onSetupForms(Am_Event_SetupForms $event) { $f = new Am_Form_Setup_ProtectDatabased($this); if($plugin_readme = $this->getReadme()) { $plugin_readme = str_replace( array('%root_url%', '%root_surl%', '%root_dir%'), array($this->getDi()->rurl(''), $this->getDi()->surl(''), $this->getDi()->root_dir), $plugin_readme); $f->addEpilog('
'.$plugin_readme.'
'); } $event->addForm($f); // addConfigItems will be called when necessary } function afterAddConfigItems(Am_Form_Setup_ProtectDatabased $form) { } function guessDbPrefix(DbSimple_Interface $db, $database=null, $prefix=null) { $res = array(); foreach ($dbs = $db->selectCol("SHOW DATABASES") as $dbname) { try { $tables = $db->selectCol("SHOW TABLES FROM ?# LIKE '%$this->guessTablePattern'", $dbname); } catch (Am_Exception_Db $e) { continue; } if (is_array($tables)) foreach ($tables as $t) { // check fields here $info = $db->select("SHOW COLUMNS FROM `$dbname`.$t"); $infostr = ""; if (is_array($info)) foreach ($info as $k => $v) $infostr .= join(';', $v) . "\n"; $wrong = 0; foreach ($this->guessFieldsPattern as $pat) { if (!preg_match('|^' . $pat . '|m', $infostr)) $wrong++; } if ($wrong) continue; $res[] = $dbname . '.' . substr($t, 0, -strlen($this->guessTablePattern)); } } return $res; } /** @return bool true if plugin is able to create customers without signup */ public function canAutoCreate() { return false; } /** @return bool true if plugin is able to create customers without signup using current user's groups from integrated script */ public function canAutoCreateFromGroups() { return false; } function configCheckDbSettings(array $config) { $class = get_class($this); $np = new $class($this->getDi(), $config); try { $db = $np->getDb(); } catch (Am_Exception_PluginDb $e) { return ___('Cannot connect to database. Check hostname, username and password settings') . ' ' . $e->getMessage(); } try { $table = $this->guessTablePattern; $fields = join(',', $this->guessFieldsPattern); $db->query("SELECT $fields FROM ?_{$table} LIMIT 1"); } catch (Am_Exception_PluginDb $e) { $defaultDb = $this->getDi()->getParameter('db'); $defaultDb = $defaultDb['mysql']['db']; $dbname = $config['db'] ? $config['db'] : $defaultDb; $prefix = $config['prefix']; return ___('Database name or prefix is wrong - could not find table [%s] with fields [%s] inside database [%s]:', $prefix.$table, $fields, $dbname) . ' ' . $e->getMessage(); } return $np->configDbChecksAdditional($db); } function configDbChecksAdditional() { } function dbErrorHandler($message, $info) { $class = 'Am_Exception_PluginDb'; $e = new $class("$message({$info['code']}) in query: {$info['query']}", @$info['code']); throw $e; } function getConfigPageId() { return get_first($this->defaultTitle, $this->getId(true)); } public function isConfigured() { return $this->getConfig('db') || $this->getConfig('prefix'); } function getGroupMode() { return $this->groupMode; } function getAdminGroups() { return array_filter(array_map('trim', $this->getConfig('admin_groups', array()))); } function getBannedGroups() { return array_filter(array_map('trim', $this->getConfig('banned_groups', array()))); } function getLockedGroup() { return $this->getConfig('locked_group'); } /** * Return plugin groups that must be set according to * aMember user subscriptions and aMember configuration * @param User $user if null, defaul group returned * @return array of int third-party group ids, or int for single-group, or true/false for GROUP_NONE */ function calculateGroups(User $user = null, $addDefault = false) { // we have got no user so search does not make sense, return default group if configured $groups = array(); if ($user && $user->pk()) { foreach ($this->getIntegrationTable()->getAllowedResources($user, $this->getId()) as $integration) { $vars = unserialize($integration->vars); $groups[] = $vars; } if ($this->groupMode == self::GROUP_NONE) return (bool) $groups; } else { if ($this->groupMode == self::GROUP_NONE) return false; } $groups = $this->chooseGroups($groups, $user); if ($addDefault && !$groups) { $ret = $this->getConfig('default_group', null); if (($this->groupMode == self::GROUP_MULTI) && (!is_array($ret))) $ret = array($ret); return $ret; } else return $groups; } /** Compare groups based on priority list */ function _compareGroups($a, $b) { $pa = array_key_exists($a, $this->_priority) ? $this->_priority[$a] : 100001; $pb = array_key_exists($b, $this->_priority) ? $this->_priority[$b] : 100000; return $pa - $pb; } /** * * @param type $groups * @param User|Am_record $user * @return type */ function addSuperGroups($groups, $user=null){ $super = $this->getConfig('super_groups'); if(!$super||!$user) return $groups; if($user instanceof User) $record = $this->getTable()->findByAmember($user); else $record = $user; if(!$record) return $groups; $old_groups = (array) $this->getTable()->getGroups($record); if(!$old_groups) return $groups; $intact = array_intersect($super, $old_groups); if(!$intact) return $groups; return array_unique(array_filter(array_merge((array)$groups, $intact))); } /** * * @param type $groups array of configs from ?_integration table * @return array of int|int return sorted array or most suitable single int */ function chooseGroups($groups, User $user = null) { $ret = array(); foreach ($groups as $config) $ret[] = $config['gr']; if($user && $user->isLocked() && ($locked = $this->getLockedGroup())) return ($this->groupMode == self::GROUP_SINGLE ? $locked : array($locked)); $ret = $this->addSuperGroups($ret, $user); if ($this->_priority === null) $this->_initPriority(); usort($ret, array($this, '_compareGroups')); if ($this->groupMode == self::GROUP_SINGLE) return $ret ? array_shift($ret) : null; else return $ret; } protected function _initPriority() { $this->_priority = array_flip($this->getConfig('priority', array())); } function getDb() { if (!$this->_db) { $dsn = array(); $dsn['scheme'] = 'mysql'; $dsn['path'] = $this->getConfig('db'); if ($this->getConfig('other_db') == "1") { $dsn = array_merge($dsn, array( 'host' => $this->getConfig('host'), 'user' => $this->getConfig('user'), 'pass' => $this->getConfig('pass'), )); } else { $appOptions = $this->getDi()->getParameters(); $dbConfig = $appOptions['db']['mysql']; $dsn = array_merge($dsn, array( 'host' => $dbConfig['host'], 'user' => $dbConfig['user'], 'pass' => $dbConfig['pass'] )); if(isset($dbConfig['port']) && $dbConfig['port']) $dsn['port'] = $dbConfig['port']; } if($dsn['host'] && (strpos($dsn['host'], ':') !== false) && preg_match('/\:(\d+)$/',$dsn['host'])) list($dsn['host'], $dsn['port']) = explode(':', $dsn['host']); $this->_db = Am_Db::connect($dsn, true); $this->_db->setErrorHandler(array($this, 'dbErrorHandler')); $this->_db->setIdentPrefix($this->getConfig('prefix')); $this->_db->query("USE ?#", $dsn['path']); $this->_db->query("SET NAMES utf8"); $this->_db->query("SET SESSION sql_mode=''"); } if ($this->sqlDebug) { if (!empty($this->getDi()->db->_logger)) $this->_db->setLogger($this->getDi()->db->_logger); } return $this->_db; } function _setDb($db) { $this->_db = $db; } /** lazy-load the table * @return Am_Protect_Table */ function getTable() { if (!$this->_table) $this->_table = $this->createTable()->setDi($this->getDi()); return $this->_table; } /** * create table * you can (in fact you must) override this function to fine-tune * @return Am_Protect_Table */ function createTable() { return new $this->_tableClass($this, $this->getDb()); } /** * create session table if applicable * @return Am_Protect_SingleLogin|null */ function createSessionTable() { return null; } /** * get session table if applicable * @return Am_Protect_SingleLogin|null */ function getSessionTable() { if(!$this->_sessionTable) $this->_sessionTable = $this->createSessionTable(); if(!is_null($this->_sessionTable)) $this->_sessionTable->setDi($this->getDi()); return $this->_sessionTable; } /** * @param Am_Event_SubscriptionChanged $event * @param User $oldUser presents if called from onUserLoginChanged */ function onSubscriptionChanged(Am_Event_SubscriptionChanged $event, User $oldUser = null) { if ($oldUser === null) $oldUser = $event->getUser(); $user = $event->getUser(); $found = $this->getTable()->findByAmember($oldUser); if ($found) { if($this->canUpdate($found)){ $this->getTable()->updateFromAmember($found, $user, $this->calculateGroups($user, true)); $pass = $this->findPassword($user, true); if ($pass) $this->getTable()->updatePassword($found, $pass); } } elseif ($groups = $this->calculateGroups($user, false)) { // we will only insert record if it makes sense - there are groups $this->getTable()->insertFromAmember($user, $this->findPassword($user, true), $groups); } } function onUserAfterUpdate(Am_Event_UserAfterUpdate $event) { $e = new Am_Event_SubscriptionChanged($event->getUser(), array(), array()); return $this->onSubscriptionChanged($e, $event->getOldUser()); } function onUserAfterDelete(Am_Event_UserAfterDelete $event) { $found = $this->getTable()->findByAmember($event->getUser()); if (!$found || !$this->canRemove($found)) return; if ($this->getConfig('remove_users')) { $this->_table->removeRecord($found); } elseif (!$this->isBanned($found)) { $this->_table->disableRecord($found, $this->calculateGroups(null, true)); } } function onSetPassword(Am_Event_SetPassword $event) { $user = $event->getUser(); $found = $this->getTable()->findByAmember($user); if ($found && $this->canUpdate($found)) { $this->_table->updatePassword($found, $event->getSaved($this->getPasswordFormat())); $user->data()->set(self::USER_NEED_SETPASS, null)->update(); } } /** * Return Object that will handle single login. If SessionTable is not created return $this * @return Am_Protect_SingleLogin */ function getSingleLoginObject(){ return is_null($sessionTable = $this->getSessionTable()) ? $this : $sessionTable; } function onAuthCheckLoggedIn(Am_Event_AuthCheckLoggedIn $event) { $record = $this->getSingleLoginObject()->getLoggedInRecord(); if (!$record || !$this->canLogin($record)) return; $user = $this->getTable()->findAmember($record); if (!$user) return; if($user->isLocked()) return; if ($this->getTable()->checkPassword($record, $user)) { $event->setSuccessAndStop($user); $this->skipAfterLogin = true; } } function onAuthAfterLogin(Am_Event_AuthAfterLogin $event) { if ($this->skipAfterLogin) return; if($event->getUser()->isLocked()) return; // there we handled situation when user was added without knowledge of password // @todo implement situation when we have found there is not password // in related user record during login if ($event->getPassword() && $event->getUser()->data()->get(self::USER_NEED_SETPASS)) { $user = $event->getUser(); $user->setPass($event->getPassword()); $user->save(); $user->data()->set(self::USER_NEED_SETPASS, null)->update(); } $record = $this->getTable()->findByAmember($event->getUser()); if (!$record || !$this->canLogin($record)) return; if (!$this->getTable()->checkPassword($record, $event->getUser(), $event->getPassword())) return; $this->getSingleLoginObject()->loginUser($record, $event->getPassword()); } function onAuthAfterLogout(Am_Event_AuthAfterLogout $event) { $this->getSingleLoginObject()->logoutUser($event->getUser()); } function onAuthSessionRefresh(Am_Event_AuthSessionRefresh $event){ $afterLoginEvent = new Am_Event_AuthAfterLogin($event->getUser()); $this->onAuthAfterLogin($afterLoginEvent); } function onCheckUniqLogin(Am_Event_CheckUniqLogin $event) { $table = $this->getTable(); if ($table->getIdType() != Am_Protect_Table::BY_LOGIN) return; if ($table->findFirstByLogin($event->getLogin())) $event->setFailureAndStop(); } function onCheckUniqEmail(Am_Event_CheckUniqEmail $event) { $table = $this->getTable(); if ($table->getIdType() != Am_Protect_Table::BY_EMAIL) return; if ($event->getUserId()) { $user = $this->getDi()->userTable->load($event->getUserId()); } else { $user = $this->getDi()->userRecord; $user->email = $event->getEmail(); } $record = $table->findByAmember($user); if ($record) { $user = $table->findAmember($record); if ($user && $user->pk()!=$event->getUserId()) $event->setFailureAndStop(); } } public function onAuthTryLogin(Am_Event_AuthTryLogin $event) { if (!$this->getConfig('auto_create')) return; //in case several plugins are using auto_create option if($event->isCreated()) return; $login = $event->getLogin(); $isEmail = preg_match('/^.+@.+\..+$/', $login); $found = !$isEmail ? $this->getTable()->findFirstByLogin($login) : $this->getTable()->findFirstByEmail($login); /* @var $found Am_Record */ if (!$found || !$this->canLogin($found)) return; // now create fake user for checkPassword $user = $this->getDi()->userTable->createRecord(); if ($isEmail) $user->email = $login; else $user->login = $login; $user->toggleFrozen(true); if (!$this->getTable()->checkPassword($found, $user, $event->getPassword())) return; // all checked, now create user in aMember $user = $this->getDi()->userRecord; $this->getTable()->createAmember($user, $found); if (!$user->login) { $this->skipCheckUniqLogin = true; $user->generateLogin(); $this->skipCheckUniqLogin = false; } $user->setPass($event->getPassword()); $user->insert(); if ($p_b = $this->getConfig('auto_create_billing_plan')) { try{ if($p_b == -1) { if($this->canAutoCreateFromGroups()) { $arr = array(); foreach($this->getConfig() as $k => $v) if(preg_match("/^auto_create_bpgroups_(.*)/",$k,$m)) $arr[$m[1]] = $v; $arr_keys = array_keys($arr); foreach($this->getTable()->getGroups($found) as $gr_id) if(in_array($gr_id, $arr_keys)) $access = $this->addAccessAfterCreate($arr[$gr_id], $user); } } else { $access = $this->addAccessAfterCreate($p_b, $user); } // send 1-day autoresponders if supposed to $this->getDi()->emailTemplateTable->sendZeroAutoresponders($user, $access); } catch (Exception $e) { //just log $this->getDi()->errorLogTable->logException($e); }; } $event->setCreated($user); } function addAccessAfterCreate($p_b,$user) { list($product_id,$billing_plan_id) = explode('_', $p_b); $invoice = $this->getDi()->invoiceRecord; $invoice->setUser($user); $product = $this->getDi()->productTable->load($product_id); $product->setBillingPlan($billing_plan_id); $invoice->add($product); $begin_date = $product->calculateStartDate($this->getDi()->sqlDate, $invoice); $p = new Am_Period($product->getBillingPlan()->first_period); $expire_date = $p->addTo($begin_date); $access = $this->getDi()->accessRecord; $access->begin_date = $begin_date; $access->expire_date = $expire_date; $access->user_id = $user->user_id; $access->product_id = $product_id; $access->comment = ___('Added during user creation by demand from plugin [%s]', $this->getId()); return $access->insert(); } function getLoggedInRecord() { return null; } function loginUser(Am_Record $record, $password) { return false; } function logoutUser(User $user) { } /** * If there is a saved password, return it * If user has plaintext pass set, encrypt it, save and return * if second paramter is true, it will return dummy record * and mark user for password update on next login * @return SavedPass|null */ public function findPassword(User $user, $returnNoPass = false) { // try find password $saved = $this->getDi()->savedPassTable->findSaved($user, $this->getPasswordFormat()); if ($saved) return $saved; /// else encrypt it again $pass = $user->getPlaintextPass(); if ($pass) { $saved = $this->getDi()->savedPassRecord; $saved->user_id = $user->user_id; $saved->format = $this->getPasswordFormat(); $saved->salt = null; $saved->pass = $this->cryptPassword($pass, $saved->salt, $user); $saved->insert(); return $saved; } // nothing if ($returnNoPass) { $pass = $this->getDi()->savedPassRecord; $pass->pass = '-nopass-' . uniqid(); $pass->salt = 'NNN'; if ($user->isLoaded()) $user->data()->set(self::USER_NEED_SETPASS, 1)->update(); return $pass; } } public function isAdmin(Am_Record $record) { if ($this->getGroupMode() == Am_Protect_Databased::GROUP_NONE) return false; return (bool)array_intersect( (array)$this->_table->getGroups($record), $this->getAdminGroups() ); } public function isBanned(Am_Record $record) { if ($this->getGroupMode() == Am_Protect_Databased::GROUP_NONE) return false; return (bool)array_intersect( (array)$this->_table->getGroups($record), $this->getBannedGroups() ); } /** * Return true if we can edit this record, and false if we can not - * for example if it is admin record or a banned record * @return bool */ function canUpdate(Am_Record $record) { return !$this->isAdmin($record) && !$this->isBanned($record); } function canRemove(Am_Record $record) { return !$this->isAdmin($record); } function canLogin(Am_Record $record) { return !$this->isAdmin($record) && !$this->isBanned($record); } function deinstall() { } /** * @see getAvailableUserGroupsSql * @see getManagedUserGroups * @return array Am_Protect_Databased_Usergroup */ function getAvailableUserGroups() { $ret = array(); if ($this->groupMode == self::GROUP_NONE) { $g = new Am_Protect_Databased_Usergroup(array( 'id'=>1, 'title'=>'Registered', 'isAdmin'=>0, 'isBanned'=>0 )); $ret[$g->getId()] = $g; return $ret; } foreach ($this->getDb()->select($this->getAvailableUserGroupsSql()) as $r) { try { $g = new Am_Protect_Databased_Usergroup($r); } catch (Am_Exception_PluginDb $e){ // to log errors with groups in integrated script $this->getDi()->errorLogTable->logException($e); throw $e; } $ret[$g->getId()] = $g; } return $ret; } function getAvailableUserGroupsSql() { throw new Am_Exception_NotImplemented("getAvailableUserGroupsSql or getAvailableUserGroups must be redefined"); } /** * return only list of user groups the script must manage: * excluding for example Banned and Admin user groups */ function getManagedUserGroups() { $groups = $this->getAvailableUserGroups(); foreach ($groups as $k => $group) if (in_array($group->getId(), $this->getConfig('admin_groups', array())) || in_array($group->getId(), $this->getConfig('banned_groups', array()))) unset($groups[$k]); return $groups; } public function getIntegrationFormElements(HTML_QuickForm2_Container $container) { $groups = $this->getManagedUserGroups(); $options = array(); foreach ($groups as $g) $options[$g->getId()] = $g->getTitle(); $container ->addSelect('gr', array(), array('options' => $options)) ->setLabel($this->getTitle() . ' usergroup'); } public function getIntegrationSettingDescription(array $config) { $groups = array_combine((array) $config['gr'], (array) $config['gr']); try { foreach ($this->getAvailableUserGroups() as $g) { $id = $g->getId(); if (!empty($groups[$id])) $groups[$id] = '[' . $g->getTitle() . ']'; } } catch (Am_Exception_PluginDb $e) { } return ___('Assign Group') . ' ' . implode(",", array_values($groups)); } public function onRebuild(Am_Event_Rebuild $event) { $batch = new Am_BatchProcessor(array($this, 'batchProcess')); $context = $event->getDoneString(); $this->_rebuildName = $this->getId(); $this->_sessionId = Zend_Session::getId(); if ($batch->run($context)) { $event->setDone(); $this->getDi()->storeRebuild->deleteByNameAndSession($this->_rebuildName, $this->_sessionId); } else { $event->setDoneString($context); } } public function batchProcess(&$context, Am_BatchProcessor $batch) { @list($step, $start) = explode('-', $context); $pageCount = 30; switch ($step) { case 0: $q = new Am_Query($this->getTable()); $count = 0; $updated = array(); foreach ($q->selectPageRecords($start / $pageCount, $pageCount) as $r) { $count++; if (!$this->canUpdate($r)) continue; /* @var $r Am_Record */ $user = $this->_table->findAmember($r); if (!$user) { // no such records in aMember, disable user record ? $this->_table->disableRecord($r, $this->calculateGroups(null, true)); } else { $updated[] = $user->user_id; $this->getTable()->updateFromAmember($r, $user, $this->calculateGroups($user, true)); $pass = $this->getDi()->savedPassTable->findSaved($user, $this->getPasswordFormat()); if ($pass) $this->getTable()->updatePassword($r, $pass); } } if (!$count) { $step++; $context = "$step-0"; }else{ $store = array(); foreach ($updated as $v) $store[] = $v; $this->getDi()->storeRebuild->setArray($this->_rebuildName, $this->_sessionId, $store, '+3 hour'); $start += $count; $context = "$step-$start"; } break; case 1: /// now select aMember users not exists in plugin db $count = 0; $records = array(); $db = $this->getDi()->db; $r = $db->queryResultOnly("SELECT t.* from ?_user t left join ?_store_rebuild s on s.user_id = t.user_id and s.rebuild_name = ? and s.session_id = ? WHERE s.user_id is null LIMIT ?d , ?d", $this->_rebuildName, $this->_sessionId, $start , $pageCount); while ($row = $db->fetchRow($r)) { $records[] = $this->getDi()->userTable->createRecord($row); } foreach ($records as $user) { $count++; /* @var $user User */ $this->onSubscriptionChanged(new Am_Event_SubscriptionChanged($user, array(), array())); } if (!$count) { $context = null; return true; } $start += $count; $context = "$step-$start"; break; default: throw new Am_Exception_InputError(___('Wrong step')); } } } class Am_Protect_Databased_Usergroup { protected $id; protected $title; protected $isAdmin = false; protected $isBanned = false; function __construct($row) { $this->id = $row['id']; $this->title = $row['title']; $this->isAdmin = (bool) @$row['is_admin']; $this->isBanned = (bool) @$row['is_banned']; if (!$this->id) throw new Am_Exception_PluginDb("Wrong group record passed - id is empty"); if (empty($this->title)) throw new Am_Exception_PluginDb("Wrong group #{$this->id} record passed - title is empty"); } function isBanned() { return $this->isBanned; } function isAdmin() { return $this->isAdmin; } function getId() { return $this->id; } function getTitle() { return $this->title; } }Table.php000064400000037771152101621640006321 0ustar00 plugin-field * mapping MUST return at least one mapping for FIELD_EMAIL, FIELD_LOGIN and FIELD_PASS
* Can also accept callbacks and constants as amember-user-field * constants should have : as first char, functions @, object methods should be passed as array()
* Pass $user object to callbacks.
* examples:
* array(':value', 'field') - field will be set to value
* array('!value', 'field') - field will be set to eval(value)
* array('@func', 'field') - func($user) will be executed, value returned by function will be assigned to field
* array(array($this, "func"), 'field') - $this->func($user) will be executed, value returned by function will be assigned to field
* * */ protected $_fieldsMapping = array(); /** @var Am_Protect_Databased */ protected $_plugin; protected $_groupMode = null; // you do not need to touch these values - it is handled automatically // based on @see $_fieldsMapping in constructor public $_passField; public $_saltField; public $_loginField; public $_emailField; /** it will be automatically changed to e-mail if there is no login field in mapping */ public $_idType = self::BY_LOGIN; /** Config of group table where groups are saved as separate records * a sample for drupal * array(self::GROUP_TABLE => '?_users_roles', self::GROUP_UID => 'uid', self::GROUP_GID => 'rid') */ protected $_groupTableConfig = array(); public function __construct(Am_Protect_Databased $plugin, $db = null, $table = null, $recordClass = null, $key = null) { $this->_plugin = $plugin; $this->_groupMode = $plugin->getGroupMode(); parent::__construct($db, $table, $recordClass, $key); if ($this->_fieldsMapping) $this->setFieldsMapping($this->_fieldsMapping); } public function getIdType() { return $this->_idType; } function setFieldsMapping(array $mapping) { $this->_fieldsMapping = $mapping; foreach ($this->_fieldsMapping as $a) { list($k, $v) = $a; if (!$this->_loginField && ($k == self::FIELD_LOGIN)) $this->_loginField = $v; if (!$this->_emailField && ($k == self::FIELD_EMAIL)) $this->_emailField = $v; if (!$this->_passField && ($k == self::FIELD_PASS)) $this->_passField = $v; if (!$this->_saltField && ($k == self::FIELD_SALT)) $this->_saltField = $v; } if (!$this->_loginField && $this->_emailField) $this->_idType = self::BY_EMAIL; } function setGroupsTableConfig(array $config) { $this->_groupTableConfig = $config; } function getFieldsMapping() { return $this->_fieldsMapping; } /** @return Am_Protect_Databased */ public function getPlugin() { return $this->_plugin; } public function findFirstByLogin($login) { $ret = $this->findFirstBy(array($this->_loginField => $login)); return $ret; } public function findFirstByEmail($email) { $ret = $this->findFirstBy(array($this->_emailField => $email)); return $ret; } /** @return Am_Record|null */ public function findByAmember(User $user) { return $this->_idType == self::BY_EMAIL ? $this->findFirstByEmail($user->email) : $this->findFirstByLogin($user->login); } /** Insert record based on aMember records * automatically fills fields from the @see getFieldsMapping() * @return Am_Record */ function createFromAmember(User $user, SavedPass $pass, $groups) { $record = $this->createRecord(); foreach ($this->_fieldsMapping as $a) { list($k, $v) = $a; switch ($k) { case self::FIELD_PASS: $record->set($v, $pass->pass); break; case self::FIELD_SALT: $record->set($v, $pass->salt); break; case self::FIELD_ADDED_SQL: $record->set($v, $user->added); break; case self::FIELD_ADDED_STAMP: $record->set($v, strtotime($user->added)); break; case self::FIELD_NAME: $record->set($v, $user->getName()); break; default: if (is_callable($k)) $val = call_user_func($k, $user, $record, $this); elseif ($k[0] == ':') $val = substr($k, 1); elseif ($k[0] == '!') $val = eval(substr($k, 1)); elseif ($k[0] != '_') $val = $user->get($k); else break; $record->set($v, is_null($val) ? "" : $val); } } return $record; } /** Update record based on updated aMember record * automatically fills fields from the @see getFieldsMapping() */ function refreshFromAmember(Am_Record $record, User $user, $groups) { foreach ($this->getFieldsMapping() as $a) { list($k, $v) = $a; // Do not update field if it is primary key for record. // Some plugins like drupal will set primary key on their own. if($v == $this->_key) continue; switch ($k) { case self::FIELD_PASS: case self::FIELD_SALT: case self::FIELD_ADDED_SQL: case self::FIELD_ADDED_STAMP: break; case self::FIELD_NAME: $record->set($v, $user->getName()); break; case self::FIELD_LOGIN: case self::FIELD_EMAIL: $record->set($v, $user->get($k)); break; default: if (is_callable($k)) $val = call_user_func($k, $user, $record, $this); elseif ($k[0] == ':') break; elseif ($k[0] == '!') $val = eval(substr($k, 1)); elseif ($k[0] != '_') $val = $user->get($k); else break; $record->set($v, is_null($val) ? "" : $val); } } } /** * Insert record to plugin based on amember record * @param User $user * @param SavedPass $pass * @param int|array|bool $groups - type depends on plugin groupsType * @return Am_Record */ function insertFromAmember(User $user, SavedPass $pass, $groups) { $record = $this->createFromAmember($user, $pass, $groups); $record->insert(); $this->setGroups($record, $groups); return $record; } public function updateFromAmember(Am_Record $record, User $user, $groups) { $this->refreshFromAmember($record, $user, $groups); $record->update(); $this->setGroups($record, $groups); } public function delete($key) { parent::delete($key); if ($this->_groupTableConfig) { $table = $this->_groupTableConfig[Am_Protect_Table::GROUP_TABLE]; $uidField = $this->_groupTableConfig[Am_Protect_Table::GROUP_UID]; $this->_db->query("DELETE FROM {$table} WHERE ?#=?", $uidField, $key); } } public function updatePassword(Am_Record $record, SavedPass $saved) { if (!$this->_passField) throw new Am_Exception_NotImplemented (get_class($this) . "->updatePassword() is not implemented"); $arr = array( $this->_passField => $saved->pass ); if ($this->_saltField) $arr[ $this->_saltField ] = $saved->salt; $record->updateQuick($arr); } public function removeRecord(Am_Record $record) { $record->delete(); } /** Set record to "default" or "disabled' state depending on config * it is exists when no related aMember record is exist in database */ public function disableRecord(Am_Record $record, $groups) { if($this->getPlugin()->getConfig('super_groups')) { $groups = $this->getPlugin()->addSuperGroups($groups, $record); } $this->setGroups($record, $groups); } /** Find related amember user based on current record login * Do not check password or groups here * @return User */ function findAmember(Am_Record $record) { return $this->_idType == Am_Protect_Table::BY_EMAIL ? $this->getDi()->userTable->findFirstByEmail($record->get($this->_emailField)) : $this->getDi()->userTable->findFirstByLogin($record->get($this->_loginField)); } /** Create amember record based on the current plugin record * automatically fills fields from the @see getFieldsMapping() * @return User returns not-inserted user */ function createAmember(User $user, Am_Record $record) { foreach ($this->getFieldsMapping() as $a) { list($k, $v) = $a; if (is_callable($k) || ($k[0] == ':') || ($k[0] == '!') || ($k[0] == '_') || $k == self::FIELD_NAME) continue; if (($vv = $record->get($v)) && !$user->get($k)) $user->set($k, $vv); } $user->data()->set("created-by-plugin", $this->_plugin->getId()); } /** * Update amember record based on the current plugin record */ function updateAmember(User $user) { } /** * Check if plaintextPass or saved password of amember user matches password * of current record * @param User $user * @return bool true if password matches */ function checkPassword(Am_Record $record, User $user, $plaintextPass = null) { if (!$this->_passField) throw new Am_Exception_NotImplemented(get_class($this) . "->checkPassword() is not implemented"); if ($plaintextPass) { $salt = $this->_saltField ? $record->get($this->_saltField) : $record->get($this->_passField); return $this->_plugin->cryptPassword($plaintextPass, $salt, $user) === $record->get($this->_passField); } else { $saved = $this->_plugin->findPassword($user); if (!$saved) return false; if ($record->get($this->_passField) != $saved->pass) return false; if ($this->_saltField && ($record->get($this->_saltField) != $saved->salt)) return false; return true; } } function getGroups(Am_Record $record) { switch ($this->_groupMode) { case Am_Protect_Databased::GROUP_NONE: return null; case Am_Protect_Databased::GROUP_MULTI: if ($this->_groupTableConfig) { $table = $this->_groupTableConfig[Am_Protect_Table::GROUP_TABLE]; $uidField = $this->_groupTableConfig[Am_Protect_Table::GROUP_UID]; $gidField = $this->_groupTableConfig[Am_Protect_Table::GROUP_GID]; return $this->_db->selectCol("SELECT $gidField FROM $table WHERE $uidField=?", $record->pk()); } break; case Am_Protect_Databased::GROUP_SINGLE: /// may be a field set $field = null; foreach ($this->_fieldsMapping as $a) { list($k, $v) = $a; if ($k == Am_Protect_Table::FIELD_GROUP_ID) { $field = $v; break; } } if ($field) { $v = $record->get($field); return $v; } break; } throw new Am_Exception_NotImplemented(get_class($this) . "->getGroups() is not implemented"); } function setGroups(Am_Record $record, $groups) { switch ($this->_groupMode) { case Am_Protect_Databased::GROUP_NONE: return null; case Am_Protect_Databased::GROUP_MULTI: if ($this->_groupTableConfig) { $this->_setTableGroups($record, $groups); return $this; } break; case Am_Protect_Databased::GROUP_SINGLE: foreach ($this->getFieldsMapping() as $a) { list($k, $v) = $a; if ($k == Am_Protect_Table::FIELD_GROUP_ID) { $record->updateQuick($v, $groups); return $this; } } break; } throw new Am_Exception_NotImplemented(get_class($this) . "->setGroups() is not implemented"); } /** * For usage within $this->setGroups() when $_groupTableConfig defined * @param array $groups - just ids to set */ protected function _setTableGroups(Am_Record $record, $groups) { $table = $this->_groupTableConfig[Am_Protect_Table::GROUP_TABLE]; $uidField = $this->_groupTableConfig[Am_Protect_Table::GROUP_UID]; $gidField = $this->_groupTableConfig[Am_Protect_Table::GROUP_GID]; $fields = empty($this->_groupTableConfig[Am_Protect_Table::GROUP_ADDITIONAL_FIELDS]) ? array() : $this->_groupTableConfig[Am_Protect_Table::GROUP_ADDITIONAL_FIELDS]; // update groups $oldGroups = $this->getGroups($record); $added = array_unique(array_diff($groups, $oldGroups)); $deleted = array_unique(array_diff($oldGroups, $groups)); if ($deleted) $this->_db->query("DELETE FROM $table WHERE $uidField=? AND $gidField IN (?a)", $record->pk(), $deleted); $sql = array(); $fk = $fv = ''; if(!empty($fields)){ $field_keys = array_keys($fields); $field_values = array_values($fields); array_walk($field_values, function(&$value, $key, $db) {$value = $db->escape($value);}, $this->_db); if(!empty($field_keys)){ $fk = ", ".join(", ", $field_keys); $fv = ", ".join(", ", $field_values); } } foreach ($added as $group) if(!is_null($group)) $sql[] = "('$group', " . $record->pk() . $fv.")"; if ($sql) $this->_db->query("INSERT INTO $table ($gidField, $uidField$fk) VALUES " . join(",", $sql)); } } Abstract.php000064400000003023152101621640007014 0ustar00 $v) $ret[] = "$k: $v"; return join('; ', $ret); } /** * @return string|null - null if no password necessary * @see SavedPass constants */ abstract public function getPasswordFormat(); /** * Crypt password for the plugin. Default implementation calls * SavedPass::crypt() method according to @see getPasswordFormat() * @see SavedPass::crypt * @return string * @param type $password * @param type $salt * @param User $user */ public function cryptPassword($pass, & $salt = null, User $user = null) { if ($this->getPasswordFormat() !== null) return $this->getDi()->savedPassTable->crypt($pass, $this->getPasswordFormat(), $salt, $user); } } SessionTable.php000064400000024655152101621640007662 0ustar00value, 'path' => value, * 'expires' => value, 'secure'=>value, * 'exact_domain' =>true|false) * All parameters to be passed to Am_Cookie::set(); */ const COOKIE_PARAMS = '_cookie_params'; const COOKIE_PARAM_DOMAIN = 'domain'; const COOKIE_PARAM_PATH = 'path'; const COOKIE_PARAM_EXPIRES = 'expires'; const COOKIE_PARAM_EXACT_DOMAIN = 'exact_domain'; const COOKIE_PARAM_SECORE = 'secure'; /* Additional fields that should be set in database when session created: array('field_name'=>$field_value or callback) */ const FIELDS_ADDITIONAL = '_additional'; /* Additional fields that should be set in database when session created: array('cookie_name' => $value or callback)) */ const COOKIES_ADDITIONAL = '_cookies_additional'; protected $_recordClass = 'Am_Protect_SessionTable_Record'; protected $_plugin = null; protected $_sessionTableConfig = null; protected $_cookie = null; protected $_sid = null; protected $_uid = null; protected $_ip = null; protected $_ua = null; protected $_created = null; protected $_changed = null; protected $_cookie_params = null; protected $_additional = null; protected $_cookies_additional = null; protected $_keyIsInt = false; protected $_defaultCookieParams = array( 'expires' => 0, 'path' => '/', 'domain' => null, 'secure' => false, 'exact_domain' => false ); function __construct(Am_Protect_Databased $plugin, $db = null, $table = null, $key = null, $recordClass = null) { $this->_plugin = $plugin; parent::__construct($db, $table, $key, $recordClass); } /** * Define configuration for Session table. * @param Array $config - array of configuration values. * Example: * array( * Am_Protect_SessionTable::FIELD_UID => 'uid', * Am_Protect_SessionTable::SESSION_COOKIE => 'cookie_name' * ); * * */ function setTableConfig(Array $config) { $this->_sessionTableConfig = $config; foreach ($this->_sessionTableConfig as $k => $v) { if (property_exists($this, $k)) $this->$k = $v; else throw new Am_Exception_Configuration("Unknown config field: $k"); } if (is_null($this->_uid) || is_null($this->_sid)) throw new Am_Exception_Configuration("Both SID and UID fields must be set in session table configuration"); } /** * Return session cookie name; * @return string */ function getSessionCookieName() { return $this->_cookie; } /** * Generate new session ID. * by default session_id will be generated this way: md5(uniqid()) * @return string */ function createSessionId() { return md5(uniqid()); } /** * Get Session ID from Cookie; * @return string */ function getSessionIdFromCookie() { return (($session_id = filterId($this->getDi()->request->getCookie($this->getSessionCookieName()))) ? $session_id : null); } /** * Return user agent of current visitor (to be stored in session table) * @return string */ function getUserAgent() { return $this->getDi()->request->getServer('HTTP_USER_AGENT'); } /** * Return IP address of current visitor (to be stored in session table) * @return string */ function getUserIp() { return $this->getDi()->request->getClientIp(); } /** * Return time to store in session table; * @return time */ function getCreateTime() { return time(); } /** * Return time to store in session table; * @return time */ function getChangeTime() { return time(); } function getUid(Am_Protect_SessionTable_Record $record) { return $record->get($this->_uid); } function setUid(Am_Protect_SessionTable_Record $record, Am_Record $user) { return $record->set($this->_uid, $user->pk()); } /** * Make sure that session is valid. Will check session IP and UserAgent * Return true if UserAgent and IP are the same as for current user. * @param Am_Record $session * @return true|false * */ function sessionIsValid(Am_Record $session) { // If session have different User Agent or IP do not login user into aMember. if (!is_null($this->_ua) && $session->get($this->_ua) && ($session->get($this->_ua) != $this->getUserAgent())) return false; if (!is_null($this->_ip) && $session->get($this->_ip) && ($session->get($this->_ip) != $this->getUserIp())) return false; return true; } /** * Load session from database; * @param $sid - session _id returned by Am_Protect_SessionTable::getSessionId * @return Am_Record|null */ function loadSession($sid) { $r = $this->findFirstBy(array($this->_sid => $sid)); if (!$r) return null; if ($this->sessionIsValid($r)) return $r; return null; } /** * Set cookie depends on _cookie_params * @param type $name - cookie name * @param type $value - cookie value */ function setCookie($name, $value) { $params = array($name, $value); if (!is_null($this->_cookie_params)) foreach (array('expires', 'path', 'domain', 'secure', 'exact_domain') as $k) $params[] = array_key_exists($k, $this->_cookie_params) && isset($this->_cookie_params[$k]) ? $this->_cookie_params[$k] : $this->_defaultCookieParams[$k]; call_user_func_array(array("Am_Cookie", "set"), $params); } /** * Delete Cookie. * @param type $name - cookie name; */ function delCookie($name) { $params = array($name, '', time() - 24 * 3600); if (!is_null($this->_cookie_params)) foreach (array('path', 'domain', 'secure', 'exact_domain') as $k) $params[] = array_key_exists($k, $this->_cookie_params) && isset($this->_cookie_params[$k]) ? $this->_cookie_params[$k] : $this->_defaultCookieParams[$k]; call_user_func_array(array("Am_Mvc_Controller", "setCookie"), $params); } /** * Delete all cookies set by plugin */ function deleteAllCookies() { $this->delCookie($this->getSessionCookieName()); if (!is_null($this->_cookies_additional)) foreach ($this->_cookies_additional as $k => $v) $this->delCookie($k); } function getLoggedInRecord() { $sid = $this->getSessionIdFromCookie(); if (is_null($sid)) return null; $session = $this->loadSession($sid); if (is_null($session)) return null; $uid = $this->getUid($session); if (!$uid) return null; return $this->_plugin->getTable()->load($uid); } function loginUser(Am_Record $record, $password) { $sid = $this->getSessionIdFromCookie(); if (is_null($sid)) { do { $sid = $this->createSessionId(); } while ($this->loadRow($sid, false)); } $session = $this->load($sid, false); if (is_null($session)) { $session = new Am_Protect_SessionTable_Record($this); $session->set($this->_sid, $sid); if (!is_null($this->_created)) $session->set($this->_created, $this->getCreateTime()); if (!is_null($this->_ip)) $session->set($this->_ip, $this->getUserIp()); if (!is_null($this->_ua)) $session->set($this->_ua, $this->getUserAgent()); } else if (!$this->sessionIsValid($session)) { return false; } $this->setUid($session, $record); if (!is_null($this->_changed)) $session->set($this->_changed, $this->getChangeTime()); if (!is_null($this->_additional)) { foreach ($this->_additional as $k => $v) { $session->set($k, is_callable($v) ? call_user_func($v, $record, $session) : $v); } } $session->save(); $this->setSessionCookie($sid, $record, $session); return true; } function setSessionCookie($sid, $record, $session){ // Update Cookies. $this->setCookie($this->getSessionCookieName(), $sid); if (!is_null($this->_cookies_additional)) foreach ($this->_cookies_additional as $k => $v) { $this->setCookie($k, is_callable($v) ? call_user_func($v, $record, $session) : $v); } } function logoutUser(User $user) { $sid = $this->getSessionIdFromCookie(); if (is_null($sid)) return; $this->deleteAllCookies(); $session = $this->loadSession($sid); if (is_null($session)) return; $session->delete(); } } class Am_Protect_SessionTable_Record extends Am_Record { // Must be able to insert record with primary key already set. protected $_disableInsertPkCheck = true; protected $_isLoaded = false; function __construct(Am_Table $table) { parent::__construct($table); } function isLoaded() { return $this->_isLoaded; } function fromRow(array $vars) { $this->_isLoaded = true; return parent::fromRow($vars); } function save() { $this->isLoaded() ? $this->update() : $this->insert(false)->refresh(); return $this; } } SingleLogin.php000064400000001532152101621640007466 0ustar00