Page 7 of 20

Posted: Sat May 20, 2006 10:32 pm
by RobertGonzalez
santosj wrote:I like this as it in turn separates it out even further giving more control to the developer. However it would still have to access the database and no information has been given on how that would be accomplished.
Everah wrote:4. Develop a barebones session handling wrapper in the event the users of this app want it, but not as a requirement to anything within the app.

If you are going to be doing Session security, then there are a lot more areas than that to consider.
We can't assume the users of this app will be using a database. What if their current setup is based on a flat file of usernames and passwords. Yes, I know it is insecure, but the possibility exists and if this app will be portable and simple, we need to approach it from that perspective (in my opinion).

I am beginning to think that session handling may not necessarily have a place in this app. I suppose we are developing an authentication app, not a session app, so maybe we should leave sessions out?

Posted: Sat May 20, 2006 10:47 pm
by Ambush Commander
Are the features you listed built-in features, or plugins?
They're "built-in" the fact that they're maintained with the same vigour as the core code, but they're plugins in that they can be disabled (and their code removed from an installation).
remember-me is pretty standard, but perhaps in a case where an application didn't need or want it, it would be nice to be able to remove the feature with a simple commenting out of the addPlugin() line.
Ideally, people using the library won't have to modify the source files at all. I'll be quite insistent on this.
It would be almost impossible to tailor the Authentication to every single predefined database type and table. The issue also is do we assert our own brand of authentication setup?
We do both. I've been thinking about this a bit, and there's two opaque layers (three, if you count PHP's system level functions, but those aren't overloadable). At any point in the layer, an advanced user can override functions to deal with their particular use case. Starting with the layer our class will interface with:

1. Action-oriented (bad term!) database wrapper, acts sort of like a facade except the fact that it contains some pseudo-SQL stuff. It contains methods that each represent an SQL query, like 'getUserFromUniqueHandle()', etc. The SQL it contains is pseudo because it doesn't actually contain the "real" column names, like "SELECT :user_id, :password FROM :table WHERE :user_handle = ?"

2. Lean DB wrapper. This actually executes the SQL and performs not only value binding but table/column name binding (in the upper case, the column bindings are textual while the data bindings are question marks). The user is able to extend the default case and inject his own column names into the class definition so everything is hunky dory.

I'd write code but I'm pressed for time. As long as compatibility problems aren't too bad, all the user has to do is configure a lean DB wrapper to process the queries for their particular schema. If they're actually writing/reading plain text, they can totally overload the action-oriented database wrapper.

You're still thinking in "Self-contained application" mode. ;-) This is "extending classes" configuration, not "pass an array" configuration.

In fact, the Lean DB wrapper doesn't have to know any connection parameters (although that would be nice). PHP's mysql_* functions have a nice feature where if a resource handle isn't specified, it'll use the last opened one. Most scripts only have one database connection. If the user is purist enough to object to this sort of trickery, let them tell the lean DB wrapper where to find the resource.

I hope this solves the problem. Maybe I missed something.
had a small epiphany
You're correct on all counts. (although, unfortunantely, that's what I believe I was arguing about the last few pages. But that's okay). Your distinction between authorization and access control is, however, quite novel. Never thought of it that way before.
I am seeing the system as several middle-men that make up the framework and modules that are provided by the application (Credential Acquisition, Authorization Rules, Datasource) using the conventions and interfaces we define.

We can provide standard Credential Acquisition (login form, captcha, cookie remember me), Authorization Rules (deny/allow all, is logged in, in group, has permission/role), Datasource (database, LDAP).
If anyone cared to take a look at the OSID links I posted above, people have already created interfaces for these problems. What they failed to do was provide working code (actually, I haven't checked, but I'm most definitely sure not code for PHP!) I cannot stress enough the importance of maintaining the code we provide.

Posted: Sat May 20, 2006 11:05 pm
by RobertGonzalez
Ambush Commander wrote:You're correct on all counts. (although, unfortunantely, that's what I believe I was arguing about the last few pages. But that's okay). Your distinction between authorization and access control is, however, quite novel. Never thought of it that way before.
I probably should have clarified my epiphany... it was that what you were saying in the last few pages finally sank in. When I went to Costco, I couldn't get in without showing my membership card (Access Control after Authentication), I couldn't buy anything without showing my card and my id (Authorization after Authentication) and I couldn't update my profile with them without verifying who I was with identification (pure Authentication).

That whole process got me to thinking that the Authentication process was an independent layer of activity while Authorization and Access Control were their own processes, but depended on Authentication. As in the case where you go to get a membership card and walk out without shopping. Just because a user logs in (Authenticates) does not mean the user needs to be Authorized or granted Access because what the user does immediately after Authentication may not warrant that type of action.

You were spot on in your previous posts. I just understood it better tonight.

Posted: Sun May 21, 2006 10:25 am
by santosj
Okay, so we are switching gears (or did we even change gears in the first place?) to extending and having a working implementation in place for the user if the coder doesn't want to do any modifications.

I was also pondering the Authentication and I believe we could also provide the form in place as a plugin also. I will also provide my own take on a possible solution, but I don't think it would meet your requirements. I'm just providing proof-of-concept.

Just the AuthForm and Pluggable Code

Code: Select all

interface AuthPlugin { }
I have this interface for checking and requiring the Interface for the plugin functions.

Code: Select all

class AuthForm implements AuthPlugin
{
	const ALLOWMULTIPLE = false; // Don't allow more than one of this class.
	
	private $action = '';
	
	public function __construct($actionscript = '')
	{
		$this->action = $actionscript;
	}
	
	// Begin the form
	public function start($actionscript = false)
	{
		$start = '';
		
		if($actionscript !== false)
		{
			$start = '<form action="'.$actionscript.'" method="post">';
		}
		else
		{
			$start = '<form action="'.$this->action.'" method="post">';
		}
		
		return $start;
	}
	
	// Return the end of the form
	public function end()
	{
		return '</form>';
	}
	
	// Return the Username input
	public function username()
	{
		return '<input type="text" name="username" />';
	}
	
	// Return the Password input
	public function password()
	{
		return '<input type="password" name="password" />';
	}
}

class Authentication
{
	private $plugins = array();
	
	// Not implemented because just the plugin parts
	public function login() { }
	public function logout() { }
	
	// Pluggable methods
	
	public function appendPlugin(AuthPlugin $plugin)
	{
		$reflection = new ReflectionClass($plugin);
		// Get the plugin name
		$name = $reflection->getName();
		
		// Prevent Same plugin Name clash
		if($reflection->hasConstant('ALLOWMULTIPLE') or $reflection->hasConstant('ALLOW_MULTIPLE'))
		{
			$allowMultiple = false;
			$constants = $reflection->getConstants();
			if(isset($constants['ALLOWMULTIPLE']) and is_bool($constants['ALLOWMULTIPLE']))
			{
				$allowMultiple = $constants['ALLOWMULTIPLE'];
			}
			else if(isset($constants['ALLOW_MULTIPLE']) and is_bool($constants['ALLOW_MULTIPLE']))
			{
				$allowMultiple = $constants['ALLOW_MULTIPLE'];
			}
			
			if($allowMultiple === false)
			{
				$this->plugins[$name] = $plugin;
			}
			else if($allowMultiple === true)
			{
				$this->plugins[$name][] = $plugin;
			}
		}
		else
		{
			$numPlugin = count($this->plugins[$name]);
			if($numPlugin == 0)
			{
				$this->plugins[$name] = $plugin;
			}
			else if($numPlugin == 1)
			{
				$save = $this->plugins[$name];
				$this->plugins[$name] = null;
				$this->plugins[$name][] = $save;
				$this->plugins[$name][] = $plugin;
			}
			else
			{
				$this->plugins[$name][] = $plugin;
			}
		}
	}
	
	public function removePlugin($name, $all = false)
	{
		if(($all === false) and (count($this->plugins[$name]) >= 1))
		{
			array_pop($this->plugins[$name]);	// remove last element
		}
		else if($all === true)
		{
			// I was told this is faster than 
			// unset($this->plugins[$name]);
			$this->plugins[$name] = null;
		}
	}
	
	/**
	 * @param string plugin the first parameter is the plugin class name
	 * @param string method the second parameter is the method of the plugin to call
	 * @param ... parameters for the function in the plugin to be called.
	 */
	public function plugin()
	{
		$numArgs = func_num_args();
		
		$className = func_get_arg(0);
		
		$numPlugins = count($this->plugins[$className]);
		
		// Use ReflectionClass to check to see if the method exists.
		$pluginReflection = new ReflectionClass($className);
		
		$methodName = func_get_arg(1);
		
		// Do not continue but do not throw exception
		if($pluginReflection->hasMethod($methodName) == false)
		{
			return false;
		}
		
		if($numPlugins == 1)
		{
			$method = $pluginReflection->getMethod($methodName);
			if($method->getNumberOfRequiredParameters() <= ($numArgs-2))
			{
				// DEBUG: I haven't tested what would happen if there are no arguments needed
				return $method->invokeArgs($className, array_slice(func_get_args(), 2, $method->getNumberOfParameters()));
			}
		}
		else if($numPlugins > 1)
		{
			// Not Implemented since it would return an array and require a loop
		}
	}
}
You then use the classes like so

Code: Select all

<?php
$authenticate = new Authentication;
$authenticate->appendPlugin(new AuthForm);

// Create form

$authenticate->plugin('AuthForm', 'start', 'confirm.php'); // assert(0 <= 1):  true
$authenticate->plugin('AuthForm', 'username'); // assert(0 <= 0):  true
$authenticate->plugin('AuthForm', 'password'); // assert(0 <= 0):  true
$authenticate->plugin('AuthForm', 'end'); // assert(0 <= 0):  true

// Remove Plugin
$authenticate->removePlugin('AuthForm');
?>

Posted: Sun May 21, 2006 12:40 pm
by Ambush Commander
Santosj, do we have to use reflection? I mean, it's cool and all, but I find it hard to follow. What exactly is the code doing?

Plus, the ALLOW_MULTIPLE and ALLOWMULTIPLE stuff is goofy. There's no need to error correct for that. Just use ALLOW_MULTIPLE and be done with it.

Posted: Sun May 21, 2006 12:47 pm
by Christopher
Ambush, I think we need some very simple examples of use cases -- no library code yet -- just examples of use. There is a form controller underneath a username/password style login (which is our common case) and we need to define how we work with the many styles of form controllers. I think you has also mentioned that you might want a simple form controller to be available to those who want to use a canned one.

For example, the classic form controller has three states: init, submitted and validated, and looks something like this:

Code: Select all

if ($request->has('submit')) {
// for submitted
    if ($form->isValid()) {
    // submitted form data is valid
        $response->redirect($form->getSuccessPage());
        return;
    } else {
    // submitted form has errors, redisplay the form with any retained values and error messages
        $error_messages = $form->getErrorMessages();
        $response->setContent($form->render());

    }
} else {
// first time in so initialize with values from somewhere if they exist
    $form->setDatasource($datasource);
    $response->setContent($form->render());
}
It seems like this system has some involvement with the $form->isValid() and the $form->getErrorMessages() parts.

Posted: Sun May 21, 2006 12:59 pm
by Ambush Commander
Here's what I think: as long as there's a reasonable seperation of domain and presentation just in that the program doesn't output any data until authentication has completed, we can simply output an authentication form and exit on the user at the most basic level. The code looks like (with // + marking things that you'd add to make it work):

Code: Select all

<?php
require('classes/AuthTools.php'); // +

mysql_connect('put', 'stuff', 'here', 'please');

authtools_quick_authorize(); // +

// USER IS NOW AUTHORIZED

// do other stuff

?>
Output <b>Page</b>
authtools_quick_authorize() will kill the script if the user isn't authorized and spit out a plain HTML form. The authorization is inanely simple: if you're authenticated, you're allowed.

The next logical step is if the user has their own UI that they want to integrate the form in. Then, it would look like...

Code: Select all

<?php
require('classes/AuthTools.php'); // +
mysql_connect('put', 'stuff', 'here', 'please');
$authorized = authtools_quick_authorize(true); // +
include('common/header.php');
if (!$authorized) { // +
    authtools_quick_authorize_showForm(); // +
    include('common/footer.php'); // +
    exit; // +
} // +

// USER IS AUTHORIZED

// other stuff
?>
This counts as template integration use cases. There are many aspects:

* Template integration
* Controller integration (somewhat connected to templates)
* DB integration
* Session integration (rare, since I suspect most people would use regular sessions)
* Request integration

The authtools_quick_* functions wrap common procedures with the underlying objects of our system. As long as the user doesn't need to swap in their own objects for most things, they can stick with functions.

Edit - Haha, I'm still thinking on a much lower level than you guys. Hmm...

Posted: Sun May 21, 2006 1:02 pm
by Christopher
Yes, but authtools_quick_authorize() is simply one implementation built from the parts of out system. So of we build the parts then creating authtools_quick_authorize() will be pretty easy.

Posted: Sun May 21, 2006 1:10 pm
by Ambush Commander
What would authtools_quick_authorize() look like?

Edit - a few extra comments to clarify

Code: Select all

// if user authenticates, use user is authorized
function authtools_quick_authorize($continue_execution = false)
{
    $credentials = new AuthTools_Credentials_Auto();
    $controller = new AuthTools_Controller();
    $command_chain = $controller->getCommandChain($credentials);
    // Command chain works this way:
    //     Depending on the credentials, multiple actions could be going on.
    // if a user has a session and a remember me token, both commands get
    // added to the chain. The application first tries to resume the
    // session: if that fails, it then tries to create a new session from
    // the remember me token. Otherwise, a failed session could preclude a
    // user from redeeming their remember me token.
    $command_chain->executeCommands();
    $auth_status = $command_chain->getAuthenticationStatus();
    $is_authenticated = $auth_status->isAuthenticated();
    $is_authorized = $is_authenticated; // is_authenticated is equivalent to is_authorized
    if ($continue_execution) {
        return $is_authorized;
    }
    if (!$is_authorized) {
        $form = new AuthTools_BasicForm($auth_status);
        echo $form->getContents();
        exit;
    }
    return true; //user is authorized
}
Edit, regarding form actions: No, I don't believe authorization has anything to do with whether or not a form submission is valid. The authorization should be controlling whether or the system even gets to check whether or not the submitted form is valid. The server firsts checks:

1. Is the user authorized to have submitted this form (outside your code snippet)
2. Is the form valid?

And then execute.

Further thinking: However, this also poses the question if you try to submit a form and my some freak accident you've been logged out, you still have to preserve the form so you log in, and then your action gets executed. So your concern has some merit.

Posted: Sun May 21, 2006 1:25 pm
by santosj
Ambush Commander wrote:Santosj, do we have to use reflection? I mean, it's cool and all, but I find it hard to follow. What exactly is the code doing?

Plus, the ALLOW_MULTIPLE and ALLOWMULTIPLE stuff is goofy. There's no need to error correct for that. Just use ALLOW_MULTIPLE and be done with it.
Well, I find reflection is easier to follow as it makes some sense. I still don't quite see the point of requiring a class in invoke() and invokeArgs() methods when the damn ReflectionMethod was returned from ReflectionClass. Why can't ReflectionClass just assign itself to the ReflectionMethod? No. I have some old code that tries this method and it is redundant. I should also comment my code more.

Yeah, I couldn't decide if ALLOWMULTIPLE or ALLOW_MULTIPLE was easier to read. I'll say ALLOW_MULTIPLE also.
arborint wrote:Ambush, I think we need some very simple examples of use cases -- no library code yet -- just examples of use. There is a form controller underneath a username/password style login (which is our common case) and we need to define how we work with the many styles of form controllers.
I agree, I'm all for further discussion, but I'm not quite seeing any code anywhere or specifications. If we are going to move this baby along, we need to start showing some code to give our opinion on. Now, I'm going to wait until Ambush is ready to show some code because all of the features together would be far beyond my own ability. So it would still be wicked sweet when finished.

Is Ambush going to code the whole thing including the Plugins or can the Plugins be up to us? Also, I'm trying to think of a good excuse to use SPL Observer, but that is going a little overboard (me thinks).
For example, the classic form controller has three states: init, submitted and validated, and looks something like this:

Code: Select all

if ($request->has('submit')) {
// for submitted
    if ($form->isValid()) {
    // submitted form data is valid
        $response->redirect($form->getSuccessPage());
        return;
    } else {
    // submitted form has errors, redisplay the form with any retained values and error messages
        $error_messages = $form->getErrorMessages();
        $response->setContent($form->render());

    }
} else {
// first time in so initialize with values from somewhere if they exist
    $form->setDatasource($datasource);
    $response->setContent($form->render());
}
It seems like this system has some involvement with the $form->isValid() and the $form->getErrorMessages() parts.
I like this method, but the point I was trying to come across with the AuthForm is that if you separate the parts out, then you can easily return it for Smarty or TemplateLite.

EDIT

The isValid() method should be part of the Authentication Class and not part of the form. Actually, I would remove the response and just use the Authentication Class instead Or is that what you had in mind anyway? The form should be separate of any database connection and should just be the Authentication Form to reduce confusion.

I like this better:

Code: Select all

interface AuthForm
{
	// Output the complete Authenication Form
	public function render();
	
	// Output the Authenication JavaScript if any
	public function getJavaScript();
}
The Username and Password errors could be Exceptions (but I wouldn't want them to be).

Code: Select all

interface AuthError
{
	public function render();

	public function __toString();
}

// Class AuthUsernameError extends Exception { }
class AuthUsernameError implements AuthError { }
// class AuthPasswordError extends Exception { }
class AuthPasswordError implements AuthError { }
Well, if you recreated the Exception class for PHP 4 you could backport with no changes if you didn't throw the error but just returned it. I don't think the error should be thrown, just returned.

DONE EDIT

Code: Select all

$templateLite->assign('StartAuth', $authenticate->plugin('AuthForm', 'start'));
//...
However in retrospect, I believe returning the entire form and table would be better since it is unlikely the user is going to use anything other than a table for the Username and Password in the first place. I +1 for your idea for the optional Authenticate Form interface.

EDIT
Ambush Commander wrote:

Code: Select all

// if user authenticates, use user is authorized
function authtools_quick_authorize($continue_execution = false)
{

}
The code is awesome. Do you have down what all of the classes are at this point?
Further thinking: However, this also poses the question if you try to submit a form and my some freak accident you've been logged out, you still have to preserve the form so you log in, and then your action gets executed. So your concern has some merit.
This would be cool, but would you serialize the form data inside an session and then when they sign back in redirect them to the last page and then unserialize the POST data inside the form? That would be cool, but even if we provide the ability, they would still need to test and use the POST for the form values.

Posted: Sun May 21, 2006 2:25 pm
by Ambush Commander
On an unrelated not (santosj, I'll get to your post later), I've been doing some research, and ElGamal encryption doesn't seem to be the best choice. We're probably going to end up using RSA (JavaScript and PHP (oh no, it's PEAR!)), but we also need to generalize the assymetrical encryption interface.

Posted: Sun May 21, 2006 2:48 pm
by Christopher
Ambush Commander wrote:Edit, regarding form actions: No, I don't believe authorization has anything to do with whether or not a form submission is valid. The authorization should be controlling whether or the system even gets to check whether or not the submitted form is valid.
Acually I was thinking that the authorization checks would be made inside the vadidation check ($form->isvalid()) not after. The validation check would be the authorization check -- the form controller would be managing the request values, passing them to the authentication code, displaying the form, etc.

I need a little time to think about the code you posted.

Posted: Sun May 21, 2006 2:51 pm
by Ambush Commander
Acually I was thinking that the authorization checks would be made inside the vadidation check ($form->isvalid()) not after. The validation check would be the authorization check -- the form controller would be managing the request values, passing them to the authentication code, displaying the form, etc.
I was going to say, "What about pages without form submission" but then I realized you were talking about Authorization ::bonk::

A few other ideas - the command chain analogy works really well when we allow one-use password authentication. There's two actions that a regular password could trigger: login() and loginFromTempPassword(). Some what analogous to OSID's "AuthenticationType". In fact, we may want to make the distinction: there are lots more: such as HTTP authentication, etc. Many different ways to "log in" a user.

Posted: Sun May 21, 2006 3:08 pm
by santosj
Indeed, I was thinking about the LDAP and htaccess, but I've never had a reason to use LDAP since I have no server to test or have reason to use.

The next one, Htaccess does everything in and of itself. It will force you to sign in using a popup form and match you against either a htpasswd file or internal username and password. One of the earlier authenication I ever tried was along the same lines, not with htaccess with with HTTP basic authentication. I was perhaps thinking that it could be possible to match against a passwd file, but those should be set with limited file permissions (if the person is intelligent) and the PHP script would most likely not be able to access to the file. Unless say it was set at 744 instead of the 600 it should be set at. It would be easy to match the username and password against a passwd file or I could be missing something, again I've never tried it.

Posted: Sun May 21, 2006 3:33 pm
by Christopher
santosj wrote:
For example, the classic form controller has three states: init, submitted and validated, and looks something like this:

Code: Select all

if ($request->has('submit')) {
// for submitted
    if ($form->isValid()) {
    // submitted form data is valid
        $response->redirect($form->getSuccessPage());
        return;
    } else {
    // submitted form has errors, redisplay the form with any retained values and error messages
        $error_messages = $form->getErrorMessages();
        $response->setContent($form->render());

    }
} else {
// first time in so initialize with values from somewhere if they exist
    $form->setDatasource($datasource);
    $response->setContent($form->render());
}
It seems like this system has some involvement with the $form->isValid() and the $form->getErrorMessages() parts.
I like this method, but the point I was trying to come across with the AuthForm is that if you separate the parts out, then you can easily return it for Smarty or TemplateLite.

EDIT

The isValid() method should be part of the Authentication Class and not part of the form. Actually, I would remove the response and just use the Authentication Class instead Or is that what you had in mind anyway? The form should be separate of any database connection and should just be the Authentication Form to reduce confusion.
I was just present that code as an example of the basic form controller logic. There is no authentication code shown and my comment was only that some of those calls would internally need to make use of our auth library. I think that is form code that just about any PHP programmer could follow -- and it does not dictate anything about the acutal presentation of the form. For example, the $form->render() method could use Smarty or TemplateLite or something else.