Flourish PHP Unframework
This is an archived copy of the forum for reference purposes

An invalidly formatted primary or unique key was passed to this object

posted by audvare 8 years ago

Creating a very basic user table and attempting to check for a user upon login. This file uses namespaces so global
is used.

DROP TABLE IF EXISTS users;
CREATE TABLE users (
  uid INTEGER PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(64) NOT NULL,
  pass VARCHAR(64) NOT NULL,
  access INTEGER UNSIGNED NOT NULL DEFAULT 0,
  created INTEGER UNSIGNED NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX uid_name ON users (uid, name);
    public function validate() {
      try {
        $hasher = new \\PasswordHash(8, TRUE);
        $hash = $hasher->HashPassword($this->post['pass']);
        
        $user = new \\Base\\User(array('name' => $this->post['name'], 'pass' => $hash));
      }
      catch (\\fNotFoundException $e) {
        die($e->getMessage());
      }
    }

I get Fatal error: Uncaught exception 'fProgrammerException' with message 'An invalidly formatted primary or unique key was passed to this User object'

With fActiveRecord you can load by the primary key or a unique key. It doesn't appear from your SQL that there is a unique constraint over (name, pass), so that will not work. Instead you need to load by uid or (uid, name).

Normally I will make the name column unique, then you can load by the name, and afterwards check the password hash. Your password hash should use a salt, and if it does, it will not be possible to use the password to load a user out of the database because the salt is used to generate the hash. See fCryptography#PasswordHashing for more info.

Here is some altered SQL that will allow you to load by name.

DROP TABLE IF EXISTS users;
CREATE TABLE users (
  uid INTEGER PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(64) NOT NULL UNIQUE,
  pass VARCHAR(64) NOT NULL,
  access INTEGER UNSIGNED NOT NULL DEFAULT 0,
  created INTEGER UNSIGNED NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

The following PHP uses fCryptography for password checking. For this code to work, you'd need to hash the password using fCryptography::hashPassword() inside of a custom setPass() method on your \\Base\\User class.

class \\Base\\User extends \\fActiveRecord
{
  // Flourish is smart enough to use this overridden method when
  // ->populate() is called on the record
  public function setPass($password)
  {
    // If the user provides a blank password, keep their current password
    // instead of hashing a blank string
    if (strlen($password) == 0) {
      // This allows for method chaining
      return $this;
    }
    return $this->set('pass', \\fCryptography::hashPassword($password));
  }
}

// ...

public function validate() {
      try {
        $user = new \\Base\\User(array('name' => $this->post['name']));
        if (!fCryptography::checkPasswordHash($this->post['pass'], $user->getPass())) {
          throw new \\fNotFoundException('The password specified is invalid');
        }
      }
      catch (\\fNotFoundException $e) {
        die($e->getMessage());
      }
    }
posted by wbond 8 years ago

Thanks, figured it out. I'm using PHPass for password hashing.

posted by audvare 8 years ago

Using PHPass is great. Just to rephrase what I said before, the output of PHPass is not deterministic due to the salt, so you would be unable to select a user out of a database using that.

posted by wbond 8 years ago