From 55c4ac1bfe565f1ca7f537fdd8b7a201be28e581 Mon Sep 17 00:00:00 2001 From: xue <> Date: Thu, 10 Nov 2005 12:47:19 +0000 Subject: Initial import of prado framework --- framework/Security/TAuthManager.php | 205 ++++++++++++++ framework/Security/TAuthorizationRule.php | 213 +++++++++++++++ framework/Security/TMembershipManager.php | 109 ++++++++ framework/Security/TStaticMembershipProvider.php | 324 +++++++++++++++++++++++ framework/Security/TUserManager.php | 192 ++++++++++++++ 5 files changed, 1043 insertions(+) create mode 100644 framework/Security/TAuthManager.php create mode 100644 framework/Security/TAuthorizationRule.php create mode 100644 framework/Security/TMembershipManager.php create mode 100644 framework/Security/TStaticMembershipProvider.php create mode 100644 framework/Security/TUserManager.php (limited to 'framework/Security') diff --git a/framework/Security/TAuthManager.php b/framework/Security/TAuthManager.php new file mode 100644 index 00000000..c12ee245 --- /dev/null +++ b/framework/Security/TAuthManager.php @@ -0,0 +1,205 @@ +_id; + } + + /** + * @param string id of this module + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param IApplication Prado application, can be null + * @param TXmlElement configuration for this module, can be null + */ + public function init($application,$config) + { + $this->_application=$application; + $application->attachEventHandler('Authentication',array($this,'doAuthentication')); + $application->attachEventHandler('EndRequest',array($this,'leave')); + $application->attachEventHandler('Authorization',array($this,'doAuthorization')); + $this->_initialized=true; + } + + public function getGuestName() + { + return $this->_guest; + } + + public function setGuestName($value) + { + $this->_guest=$value; + } + + public function getUserManager() + { + if($this->_users instanceof TUserManager) + return $this->_users; + else + { + if(($users=$this->_application->getModule($this->_users))===null) + throw new TConfigurationException('authenticator_usermanager_inexistent',$this->_users); + if(!($users instanceof TUserManager)) + throw new TConfigurationException('authenticator_usermanager_invalid',$this->_users); + $this->_users=$users; + return $users; + } + } + + public function setUserManager($provider) + { + $this->_users=$provider; + } + + public function getLoginPage() + { + return $this->_loginPage; + } + + public function setLoginPage($pagePath) + { + $this->_loginPage=$pagePath; + } + + public function doAuthentication($sender,$param) + { + $this->onAuthenticate($param); + + $service=$this->_application->getService(); + if(($service instanceof TPageService) && $service->isRequestingPage($this->getLoginPage())) + $this->_skipAuthorization=true; + } + + public function doAuthorization($sender,$param) + { + if(!$this->_skipAuthorization) + { + $this->onAuthorize($param); + } + } + + public function leave($sender,$param) + { + if($this->_application->getResponse()->getStatusCode()===401) + { + $service=$this->_application->getService(); + if($service instanceof TPageService) + { + $returnUrl=$this->_application->getRequest()->getRequestUri(); + $url=$service->constructUrl($this->getLoginPage(),array(self::RETURN_URL_VAR=>$returnUrl)); + $this->_application->getResponse()->redirect($url); + } + } + } + + public function onAuthenticate($param) + { + if($this->hasEventHandler('Authenticate')) + $this->raiseEvent('Authenticate',$this,$this->_application); + if($this->_application->getUser()!==null) + return; + + if(($session=$this->_application->getSession())===null) + throw new TConfigurationException('authenticator_session_required'); + $session->open(); + if(($userManager=$this->getUserManager())===null) + throw new TConfigurationException('authenticator_usermanager_required'); + $sessionInfo=$session->getItems()->itemAt($this->generateUserSessionKey()); + $user=$userManager->getUser(null)->loadFromString($sessionInfo); + $this->_application->setUser($user); + } + + public function onAuthorize($param) + { + if($this->hasEventHandler('Authenticate')) + $this->raiseEvent('Authorize',$this,$this->_application); + if($this->_authRules!==null && !$this->_authRules->isUserAllowed($this->_application->getUser(),$this->_application->getRequest()->getRequestType())) + { + $this->_application->getResponse()->setStatusCode(401); + $this->_application->completeRequest(); + } + } + + protected function generateUserSessionKey() + { + return md5($this->_application->getUniqueID().'prado:user'); + } + + public function updateSessionUser($user) + { + if(!$user->getIsGuest()) + { + if(($session=$this->_application->getSession())===null) + throw new TConfigurationException('authenticator_session_required'); + else + $session->getItems()->add($this->generateUserSessionKey(),$user->saveToString()); + } + } + + public function login($username,$password) + { + if(($userManager=$this->getUserManager())===null) + throw new TConfigurationException('authenticator_usermanager_required'); + else + { + if($userManager->validateUser($username,$password)) + { + $user=$userManager->getUser($username); + $this->updateSessionUser($user); + $this->_application->setUser($user); + return true; + } + else + return false; + } + } + + public function logout() + { + if(($userManager=$this->getUserManager())===null) + throw new TConfigurationException('authenticator_usermanager_required'); + else if(($session=$this->_application->getSession())===null) + throw new TConfigurationException('authenticator_session_required'); + else + { + $userManager->logout($this->_application->getUser()); + $session->destroy(); + } + } + /** + * @return TAuthorizationRuleCollection list of authorization rules that may be applied + */ + + public function getAuthorizationRules() + { + if($this->_authRules===null) + $this->_authRules=new TAuthorizationRuleCollection; + return $this->_authRules; + } +} + +?> \ No newline at end of file diff --git a/framework/Security/TAuthorizationRule.php b/framework/Security/TAuthorizationRule.php new file mode 100644 index 00000000..2ee6de49 --- /dev/null +++ b/framework/Security/TAuthorizationRule.php @@ -0,0 +1,213 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Security + */ +/** + * TAuthorizationRule class + * + * TAuthorizationRule represents a single authorization rule. + * A rule is specified by an action (required), a list of users (optional), + * a list of roles (optional), and a verb (optional). + * Action can be either 'allow' or 'deny'. + * Guest (anonymous, unauthenticated) users are represented by question mark '?'. + * All users (including guest users) are represented by asterisk '*'. + * Users/roles are case-insensitive. + * Different users/roles are separated by comma ','. + * Verb can be either 'get' or 'post'. If it is absent, it means both. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Security + * @since 3.0 + */ +class TAuthorizationRule extends TComponent +{ + /** + * @var string action, either 'allow' or 'deny' + */ + private $_action; + /** + * @var array list of user IDs + */ + private $_users; + /** + * @var array list of roles + */ + private $_roles; + /** + * @var string verb, may be empty, 'get', or 'post'. + */ + private $_verb; + /** + * @var boolean if this rule applies to everyone + */ + private $_everyone; + /** + * @var boolean if this rule applies to guest user + */ + private $_guest; + + /** + * Constructor. + * @param string action, either 'deny' or 'allow' + * @param string a comma separated user list + * @param string a comma separated role list + * @param string verb, can be empty, 'get', or 'post' + */ + public function __construct($action,$users,$roles,$verb='') + { + parent::__construct(); + $action=strtolower(trim($action)); + if($action==='allow' || $action==='deny') + $this->_action=$action; + else + throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); + $this->_users=array(); + $this->_roles=array(); + $this->_everyone=false; + $this->_guest=false; + foreach(explode(',',$users) as $user) + { + if(($user=trim(strtolower($user)))!=='') + { + if($user==='*') + $this->_everyone=true; + else if($user==='?') + $this->_guest=true; + else + $this->_users[]=$user; + } + } + foreach(explode(',',$roles) as $role) + { + if(($role=trim(strtolower($role)))!=='') + $this->_roles[]=$role; + } + $verb=trim(strtolower($verb)); + if($verb==='' || $verb==='get' || $verb==='post') + $this->_verb=$verb; + else + throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); + } + + /** + * @return string action, either 'allow' or 'deny' + */ + public function getAction() + { + return $this->_action; + } + + /** + * @return array list of user IDs + */ + public function getUsers() + { + return $this->_users; + } + + /** + * @return array list of roles + */ + public function getRoles() + { + return $this->_roles; + } + + /** + * @return string verb, may be empty, 'get', or 'post'. + */ + public function getVerb() + { + return $this->_verb; + } + + /** + * @return boolean if this rule applies to everyone + */ + public function getGuestApplied() + { + return $this->_guest; + } + + /** + * @var boolean if this rule applies to everyone + */ + public function getEveryoneApplied() + { + return $this->_everyone; + } + + /** + * @return integer 1 if the user is allowed, -1 if the user is denied, 0 if the rule does not apply to the user + */ + public function isUserAllowed(IUser $user,$verb) + { + $decision=($this->_action==='allow')?1:-1; + if($this->_verb==='' || strcasecmp($verb,$this->_verb)===0) + { + if($this->_everyone || ($this->_guest && $user->getIsGuest())) + return $decision; + if(in_array(strtolower($user->getName()),$this->_users)) + return $decision; + foreach($this->_roles as $role) + if($user->isInRole($role)) + return $decision; + } + return 0; + } +} + + +/** + * TAuthorizationRuleCollection class. + * TAuthorizationRuleCollection represents a collection of authorization rules {@link TAuthorizationRule}. + * To check if a user is allowed, call {@link isUserAllowed}. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Security + * @since 3.0 + */ +class TAuthorizationRuleCollection extends TList +{ + /** + * @param IUser the user to be authorized + * @param string verb, can be empty, 'post' or 'get'. + * @return boolean whether the user is allowed + */ + public function isUserAllowed($user,$verb) + { + if($user instanceof IUser) + { + $verb=strtolower(trim($verb)); + foreach($this as $rule) + { + if(($decision=$rule->isUserAllowed($user,$verb))!==0) + return ($decision>0); + } + return true; + } + else + return false; + } + + /** + * Ensures that only instance of TAuthorizationRule is added to the collection. + * @param mixed item to be added to the collection + * @return boolean whether the item can be added to the collection + */ + protected function canAddItem($item) + { + return ($item instanceof TAuthorizationRule); + } +} + +?> \ No newline at end of file diff --git a/framework/Security/TMembershipManager.php b/framework/Security/TMembershipManager.php new file mode 100644 index 00000000..a3c1ae55 --- /dev/null +++ b/framework/Security/TMembershipManager.php @@ -0,0 +1,109 @@ + \ No newline at end of file diff --git a/framework/Security/TStaticMembershipProvider.php b/framework/Security/TStaticMembershipProvider.php new file mode 100644 index 00000000..857a8f26 --- /dev/null +++ b/framework/Security/TStaticMembershipProvider.php @@ -0,0 +1,324 @@ +using System; +using System.Xml; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Web.Security; +using System.Web.Hosting; +using System.Web.Management; +using System.Security.Permissions; +using System.Web; + +public class ReadOnlyXmlMembershipProvider : MembershipProvider +{ + private Dictionary _Users; + private string _XmlFileName; + + // MembershipProvider Properties + public override string ApplicationName + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override bool EnablePasswordRetrieval + { + get { return false; } + } + + public override bool EnablePasswordReset + { + get { return false; } + } + + public override int MaxInvalidPasswordAttempts + { + get { throw new NotSupportedException(); } + } + + public override int MinRequiredNonAlphanumericCharacters + { + get { throw new NotSupportedException(); } + } + + public override int MinRequiredPasswordLength + { + get { throw new NotSupportedException(); } + } + + public override int PasswordAttemptWindow + { + get { throw new NotSupportedException(); } + } + + public override MembershipPasswordFormat PasswordFormat + { + get { throw new NotSupportedException(); } + } + + public override string PasswordStrengthRegularExpression + { + get { throw new NotSupportedException(); } + } + + public override bool RequiresQuestionAndAnswer + { + get { throw new NotSupportedException(); } + } + + public override bool RequiresUniqueEmail + { + get { throw new NotSupportedException(); } + } + + // MembershipProvider Methods + public override void Initialize (string name, + NameValueCollection config) + { + // Verify that config isn't null + if (config == null) + throw new ArgumentNullException ("config"); + + // Assign the provider a default name if it doesn't have one + if (String.IsNullOrEmpty (name)) + name = "ReadOnlyXmlMembershipProvider"; + + // Add a default "description" attribute to config if the + // attribute doesn't exist or is empty + if (string.IsNullOrEmpty (config["description"])) { + config.Remove ("description"); + config.Add ("description", + "Read-only XML membership provider"); + } + + // Call the base class's Initialize method + base.Initialize(name, config); + + // Initialize _XmlFileName and make sure the path + // is app-relative + string path = config["xmlFileName"]; + + if (String.IsNullOrEmpty (path)) + path = "~/App_Data/Users.xml"; + + if (!VirtualPathUtility.IsAppRelative(path)) + throw new ArgumentException + ("xmlFileName must be app-relative"); + + string fullyQualifiedPath = VirtualPathUtility.Combine + (VirtualPathUtility.AppendTrailingSlash + (HttpRuntime.AppDomainAppVirtualPath), path); + + _XmlFileName = HostingEnvironment.MapPath(fullyQualifiedPath); + config.Remove ("xmlFileName"); + + // Make sure we have permission to read the XML data source and + // throw an exception if we don't + FileIOPermission permission = + new FileIOPermission(FileIOPermissionAccess.Read, + _XmlFileName); + permission.Demand(); + + // Throw an exception if unrecognized attributes remain + if (config.Count > 0) { + string attr = config.GetKey (0); + if (!String.IsNullOrEmpty (attr)) + throw new ProviderException + ("Unrecognized attribute: " + attr); + } + } + + public override bool ValidateUser(string username, string password) + { + // Validate input parameters + if (String.IsNullOrEmpty(username) || + String.IsNullOrEmpty(password)) + return false; + + try + { + // Make sure the data source has been loaded + ReadMembershipDataStore(); + + // Validate the user name and password + MembershipUser user; + if (_Users.TryGetValue (username, out user)) + { + if (user.Comment == password) // Case-sensitive + { + // NOTE: A read/write membership provider + // would update the user's LastLoginDate here. + // A fully featured provider would also fire + // an AuditMembershipAuthenticationSuccess + // Web event + return true; + } + } + + // NOTE: A fully featured membership provider would + // fire an AuditMembershipAuthenticationFailure + // Web event here + return false; + } + catch (Exception) + { + return false; + } + } + + public override MembershipUser GetUser(string username, + bool userIsOnline) + { + // Note: This implementation ignores userIsOnline + + // Validate input parameters + if (String.IsNullOrEmpty(username)) + return null; + + // Make sure the data source has been loaded + ReadMembershipDataStore(); + + // Retrieve the user from the data source + MembershipUser user; + if (_Users.TryGetValue (username, out user)) + return user; + + return null; + } + + public override MembershipUserCollection GetAllUsers(int pageIndex, + int pageSize, out int totalRecords) + { + // Note: This implementation ignores pageIndex and pageSize, + // and it doesn't sort the MembershipUser objects returned + + // Make sure the data source has been loaded + ReadMembershipDataStore(); + + MembershipUserCollection users = + new MembershipUserCollection(); + + foreach (KeyValuePair pair in _Users) + users.Add(pair.Value); + + totalRecords = users.Count; + return users; + } + + public override int GetNumberOfUsersOnline() + { + throw new NotSupportedException(); + } + + public override bool ChangePassword(string username, + string oldPassword, string newPassword) + { + throw new NotSupportedException(); + } + + public override bool + ChangePasswordQuestionAndAnswer(string username, + string password, string newPasswordQuestion, + string newPasswordAnswer) + { + throw new NotSupportedException(); + } + + public override MembershipUser CreateUser(string username, + string password, string email, string passwordQuestion, + string passwordAnswer, bool isApproved, object providerUserKey, + out MembershipCreateStatus status) + { + throw new NotSupportedException(); + } + + public override bool DeleteUser(string username, + bool deleteAllRelatedData) + { + throw new NotSupportedException(); + } + + public override MembershipUserCollection + FindUsersByEmail(string emailToMatch, int pageIndex, + int pageSize, out int totalRecords) + { + throw new NotSupportedException(); + } + + public override MembershipUserCollection + FindUsersByName(string usernameToMatch, int pageIndex, + int pageSize, out int totalRecords) + { + throw new NotSupportedException(); + } + + public override string GetPassword(string username, string answer) + { + throw new NotSupportedException(); + } + + public override MembershipUser GetUser(object providerUserKey, + bool userIsOnline) + { + throw new NotSupportedException(); + } + + public override string GetUserNameByEmail(string email) + { + throw new NotSupportedException(); + } + + public override string ResetPassword(string username, + string answer) + { + throw new NotSupportedException(); + } + + public override bool UnlockUser(string userName) + { + throw new NotSupportedException(); + } + + public override void UpdateUser(MembershipUser user) + { + throw new NotSupportedException(); + } + + // Helper method + private void ReadMembershipDataStore() + { + lock (this) + { + if (_Users == null) + { + _Users = new Dictionary + (16, StringComparer.InvariantCultureIgnoreCase); + XmlDocument doc = new XmlDocument(); + doc.Load(_XmlFileName); + XmlNodeList nodes = doc.GetElementsByTagName("User"); + + foreach (XmlNode node in nodes) + { + MembershipUser user = new MembershipUser( + Name, // Provider name + node["UserName"].InnerText, // Username + null, // providerUserKey + node["EMail"].InnerText, // Email + String.Empty, // passwordQuestion + node["Password"].InnerText, // Comment + true, // isApproved + false, // isLockedOut + DateTime.Now, // creationDate + DateTime.Now, // lastLoginDate + DateTime.Now, // lastActivityDate + DateTime.Now, // lastPasswordChangedDate + new DateTime(1980, 1, 1) // lastLockoutDate + ); + + _Users.Add(user.UserName, user); + } + } + } + } +} + diff --git a/framework/Security/TUserManager.php b/framework/Security/TUserManager.php new file mode 100644 index 00000000..882c5d5c --- /dev/null +++ b/framework/Security/TUserManager.php @@ -0,0 +1,192 @@ + + * @version $Revision: $ $Date: $ + * @package System.Security + * @since 3.0 + */ +interface IUser +{ + public function getManager(); + public function getName(); + public function setName($value); + public function getIsGuest(); + public function setIsGuest($value); + public function getRoles(); + public function setRoles($value); + /** + * @param string role to be tested + * @return boolean whether the user is of this role + */ + public function isInRole($role); + public function saveToString(); + public function loadFromString($string); +} + +class TUser extends TComponent implements IUser +{ + private $_manager; + private $_isGuest=false; + private $_name=''; + private $_roles=array(); + + public function __construct($manager=null) + { + parent::__construct(); + $this->_manager=$manager; + } + + public function getManager() + { + return $this->_manager; + } + + public function getName() + { + return $this->_name; + } + + public function setName($value) + { + $this->_name=$value; + } + + public function getIsGuest() + { + return $this->_isGuest; + } + + public function setIsGuest($value) + { + $this->_isGuest=TPropertyValue::ensureBoolean($value); + if($this->_isGuest) + { + $this->_name=''; + $this->_roles=array(); + } + } + + public function getRoles() + { + return $this->_roles; + } + + public function setRoles($value) + { + if(is_array($value)) + $this->_roles=$value; + else + { + $this->_roles=array(); + foreach(explode(',',$value) as $role) + $this->_roles[]=trim($value); + } + } + + public function isInRole($role) + { + return in_array($role,$this->_roles); + } + + public function saveToString() + { + return serialize(array($this->_name,$this->_roles,$this->_isGuest)); + } + + public function loadFromString($data) + { + if(!empty($data)) + { + $array=unserialize($data); + $this->_name=$array[0]; + $this->_roles=$array[1]; + $this->_isGuest=$array[2]; + } + return $this; + } +} + + +class TUserManager extends TComponent implements IModule +{ + private $_id; + private $_users=array(); + private $_guestName='Guest'; + private $_passwordMode='MD5'; + + public function init($application,$config) + { + foreach($config->getElementsByTagName('user') as $node) + $this->_users[$node->getAttribute('name')]=$node->getAttribute('password'); + } + + public function getID() + { + return $this->_id; + } + + public function setID($value) + { + $this->_id=$value; + } + + public function getGuestName() + { + return $this->_guestName; + } + + public function setGuestName($value) + { + $this->_guestName=$value; + } + + public function getPasswordMode() + { + return $this->_passwordMode; + } + + public function setPasswordMode($value) + { + $this->_passwordMode=TPropertyValue::ensureEnum($value,array('Clear','MD5','SHA1')); + } + + public function validateUser($username,$password) + { + if($this->_passwordMode==='MD5') + $password=md5($password); + else if($this->_passwordMode==='SHA1') + $password=sha1($password); + return (isset($this->_users[$username]) && $this->_users[$username]===$password); + } + + public function logout($user) + { + $user->setIsGuest(true); + $user->setName($this->getGuestName()); + } + + public function getUser($username=null) + { + if($username===null) + { + $user=new TUser($this); + $user->setIsGuest($username===null); + return $user; + } + else if(isset($this->_users[$username])) + { + $user=new TUser($this); + $user->setName($username); + return $user; + } + else + return null; + } +} + +?> \ No newline at end of file -- cgit v1.2.3