/** * @param string $s_Name The name of the attribute * * @return mixed The attribute or null if it does not exist */ public function __get($s_Name) { switch ($s_Name) { case 'id': return $this->_o_Config->id; case 'status': return $this->_o_ClientEntity->status; case 'password': return $this->_o_ClientEntity->getRSAEncryptedPassword(); default: return null; } }
/** * Creates a new client record and exports the data to the configured client export directory * * @return mixed The new client's ID or false on failure */ public function createClient() { $s_NewID = ClientEntity::getNewID(); $s_NewPath = $this->client_export_path . $s_NewID . DIRECTORY_SEPARATOR; $s_NewDBPath = $s_NewPath . $s_NewID . '.db'; $s_NewConfigPath = $s_NewPath . 'config.xml'; /* Create client directory */ if (!file_exists($s_NewPath) && !mkdir($s_NewPath, (int) $this->dir_umask, true)) { return false; } elseif (!is_writeable($s_NewPath)) { return false; } /* Initialize client data */ $s_NewDBKey = sha1(openssl_random_pseudo_bytes(4096)); $a_NewData['id'] = $s_NewID; $a_NewData['server_public_key'] = (string) $this->public_key; // Randomizing the initial counter sequence makes brute force attacks harder $a_NewData['counter'] = hexdec(bin2hex(openssl_random_pseudo_bytes(1))); $a_NewData['key'] = sha1(openssl_random_pseudo_bytes(4096)); $a_NewData['password_length'] = (int) $this->password_length; $a_NewData['status'] = ClientEntity::ACTIVE; $a_NewData['failed_auths'] = 0; /* Write the files to the client directory */ $o_ClientConfig = new SimpleXMLElement('<otpclient></otpclient>'); $o_ClientConfig->addChild('id', $a_NewData['id']); $o_ClientConfig->addChild('db_key', $s_NewDBKey); $o_ClientConfig->addChild('db_path', $s_NewDBPath); file_put_contents($s_NewConfigPath, $o_ClientConfig->asXML()); chmod($s_NewConfigPath, (int) $this->file_umask); copy($this->client_export_path . 'OTPClient.php', $s_NewPath . 'OTPClient.php'); chmod($s_NewPath . 'OTPClient.php', (int) $this->file_umask); copy($this->client_export_path . 'ClientEntity.php', $s_NewPath . 'ClientEntity.php'); chmod($s_NewPath . 'ClientEntity.php', (int) $this->file_umask); // Create client's database file $o_ClientDB = new PDO('sqlite:' . $s_NewDBPath); $o_ClientDB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); chmod($s_NewDBPath, (int) $this->file_umask); /* Create the new client records */ ClientEntity::save($a_NewData, $this->_o_DB, $this->db_key); $o_Result = $this->_o_DB->query("SELECT sql FROM sqlite_master WHERE tbl_name='clients'"); $a_Result = $o_Result->fetch(PDO::FETCH_NUM); $s_CreateTableQuery = $a_Result[0]; $o_ClientDB->exec($s_CreateTableQuery); ClientEntity::save($a_NewData, $o_ClientDB, $s_NewDBKey); /* The RFC specifies that the client should increment its counter before sending a password, but the server should only increment the counter AFTER a successful authentication attempt. This ensures that the server-side record is one count ahead of the client-side record to facilitate this requirement */ $NewClient = new ClientEntity($this->_o_DB, $a_NewData['id'], $this->db_key); $NewClient->counter++; return $s_NewConfigPath; }
/** * @test save * @dataProvider createClasses */ public function testSaveCompleteData($o_Config, $o_ClientEntity, $o_DB) { $a_ClientData['id'] = $o_ClientEntity->id; $a_ClientData['server_public_key'] = $o_ClientEntity->server_public_key; $a_ClientData['counter'] = $o_ClientEntity->counter; $a_ClientData['key'] = $o_ClientEntity->key; $a_ClientData['password_length'] = $o_ClientEntity->password_length; $a_ClientData['status'] = ClientEntity::ACTIVE; $a_ClientData['failed_auths'] = $o_ClientEntity->failed_auths; $a_EncryptedData = ClientEntity::save($a_ClientData, $o_DB, $o_Config->db_key); $this->assertEquals(count($a_EncryptedData), count($a_ClientData)); }