Explorar o código

Started implementing Security class, and added some documentation on what the result should be

Taddeus Kroes %!s(int64=13) %!d(string=hai) anos
pai
achega
80bec81576
Modificáronse 2 ficheiros con 208 adicións e 0 borrados
  1. 195 0
      security.php
  2. 13 0
      tests/test_security.php

+ 195 - 0
security.php

@@ -0,0 +1,195 @@
+<?php
+/**
+ * Security functions for user authentication and authorization.
+ * 
+ * Usage example:
+ * <code>
+ * try {
+ *     $security = webbasics\Security::getInstance();
+ *     
+ *     // Authentication: can the origin of the request be trusted?
+ *     
+ *     // Verify that a user is logged in
+ *     $security->requireLogin();
+ *     
+ *     // Use a security token to verify that the request originated from a
+ *     // trusted page. This is recommended if, for example, the script makes
+ *     // changes to the database
+ *     $security->requireToken($_REQUEST['token']);
+ *     
+ *     // Authorization: is the user allowed to request this page?
+ *     $security->requireUserRole('admin');
+ *     
+ *     ...
+ *     
+ *     // Pass token to template so that it can be used in a submitted form or
+ *     // AJAX request
+ *     $template->set('token', $auth->generateToken());
+ *     
+ *     ...
+ *     
+ * } catch(webbasics\AuthenticationFailed $e) {
+ *     die('Get lost hacker!');
+ * } catch(webbasics\AuthorizationFailed $e) {
+ *     http_response_code(403);
+ *     die('You are not authorized to view this page.');
+ * }
+ * </code>
+ * 
+ * Corresponding login controller example:
+ * <code>
+ * // Find the user using ActiveRecord (not part of the WebBasics library)
+ * $user = User::first(array('username' => $_POST['username']));
+ * 
+ * if (!$user)
+ *     die('Invalid username');
+ * 
+ * // Current user is part of the 
+ * $security = webbasics\Security::getInstance();
+ * $security->setUser($user);
+ * 
+ * // Simple: use a plain password
+ * if (!$security->attemptPassword($user, $_POST['password']))
+ *     die('Invalid password');
+ * 
+ * // More secure: hash the password in a javascript function before
+ * // submitting the login form
+ * if (!$security->attemptPasswordHash($user, $_POST['password_hash']))
+ *     die('Invalid password');
+ * </code>
+ * 
+ * And the User model implementation used in the example above:
+ * <code>
+ * use PHPActiveRecord\Model;
+ * use webbasics\AuthenticatedUser;
+ * use webbasics\AuthorizedUser;
+ * 
+ * class User extends Model implements AuthenticatedUser, AuthorizedUser {
+ *     function getUsername() {
+ *         return $this->username;
+ *     }
+ *     
+ *     function getPasswordHash() {
+ *         return $this->password;
+ *     }
+ *     
+ *     function getCookieToken() {
+ *         return $this->cookie_token;
+ *     }
+ *     
+ *     function setCookieToken($token) {
+ *         $this->update_attribute('cookie_token', $token);
+ *     }
+ *     
+ *     function getRegistrationToken() {
+ *         return $this->registration_token;
+ *     }
+ *     
+ *     function setRegistrationToken($token) {
+ *         $this->update_attribute('registration_token', $token);
+ *     }
+ *     
+ *     function getRole() {
+ *         return $this->role;
+ *     }
+ * }
+ * </code>
+ * 
+ * @author Taddeus Kroes
+ * @date 05-10-2012
+ */
+
+namespace webbasics;
+
+require_once 'base.php';
+
+interface AuthenticatedUser {
+	function getUsername();
+	function getPasswordHash();
+	
+	function getCookieToken();
+	function setCookieToken($token);
+	
+	function getRegistrationToken();
+	function setRegistrationToken($token);
+}
+
+interface AuthorizedUser {
+	function getRole();
+}
+
+class Security {
+	const SESSION_TOKEN_NAME = 'auth_token';
+	const SESSION_NAME_USERDATA = 'auth_userdata';
+	
+	private static $instance;
+	
+	private $user;
+	
+	static function getInstance() {
+		if (self::$instance === null)
+			self::$instance = new self;
+		
+		return self::$instance;
+	}
+	
+	private function __construct() {}
+	
+	function generateToken() {
+		$session = Session::getInstance();
+		$token = sha1(self::generateRandomString(10));
+		$session->set(self::SESSION_TOKEN_NAME, $token);
+		return $token;
+	}
+	
+	function requireToken($request_token) {
+		if ($request_token != $this->getSavedToken())
+			throw new AuthenticationFailed('invalid token "%s"', $request_token);
+	}
+	
+	private function getSavedToken() {
+		$session = Session::getInstance();
+		
+		if (!$session->isRegistered(self::SESSION_TOKEN_NAME))
+			throw new AuthenticationError('no token saved in session');
+		
+		return $session->get(self::SESSION_TOKEN_NAME);
+	}
+	
+	function sessionDataExists() {
+		return Session::getInstance()->areRegistered(array(
+			self::SESSION_TOKEN_NAME, self::SESSION_NAME_USERDATA));
+	}
+	
+	function requireLogin() {
+		
+	}
+	
+	function requireUserRole() {
+		
+	}
+	
+	//function setUser(AuthenticatedUser $user) {
+	//	$this->user = $user;
+	//}
+	
+	static function generateRandomString($length) {
+		$CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUWXYZ01234567890123456789';
+		$string = '';
+		
+		srand(time());
+		
+		for ($i = 0; $i < $length; $i++)
+			$string .= $CHARS[rand(0, strlen($CHARS) - 1)];
+		
+		return $string;
+	}
+}
+
+class AuthenticationError extends FormattedException {}
+class AuthenticationFailed extends FormattedException {}
+
+class AuthorizationError extends FormattedException {}
+class AuthorizationFailed extends FormattedException {}
+
+?>

+ 13 - 0
tests/test_security.php

@@ -0,0 +1,13 @@
+<?php
+
+require_once 'SingletonTestCase.php';
+require_once 'security.php';
+use webbasics\Security;
+
+class SecurityTest extends SingletonTestCase {
+	function getClassName() {
+		return 'webbasics\Security';
+	}
+}
+
+?>