Refactored CredentialManager & Vault
https://git.n64.cc/nosial/ncc/-/issues/27
This commit is contained in:
parent
274f33a5c9
commit
273d4b6612
8 changed files with 677 additions and 244 deletions
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
namespace ncc\Abstracts;
|
namespace ncc\Abstracts;
|
||||||
|
|
||||||
abstract class RemoteAuthenticationType
|
abstract class AuthenticationType
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* A combination of a username and password is used for authentication
|
* A combination of a username and password is used for authentication
|
||||||
*/
|
*/
|
||||||
const UsernamePassword = 'USERNAME_PASSWORD';
|
const UsernamePassword = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single private access token is used for authentication
|
* A single private access token is used for authentication
|
||||||
*/
|
*/
|
||||||
const PrivateAccessToken = 'PRIVATE_ACCESS_TOKEN';
|
const AccessToken = 2;
|
||||||
}
|
}
|
30
src/ncc/Interfaces/PasswordInterface.php
Normal file
30
src/ncc/Interfaces/PasswordInterface.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ncc\Interfaces;
|
||||||
|
|
||||||
|
use ncc\Abstracts\AuthenticationType;
|
||||||
|
|
||||||
|
interface PasswordInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param bool $bytecode
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(bool $bytecode=false): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $data): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAuthenticationType(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string;
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
use ncc\Abstracts\Scopes;
|
use ncc\Abstracts\Scopes;
|
||||||
use ncc\Abstracts\Versions;
|
use ncc\Abstracts\Versions;
|
||||||
use ncc\Exceptions\AccessDeniedException;
|
use ncc\Exceptions\AccessDeniedException;
|
||||||
use ncc\Exceptions\InvalidCredentialsEntryException;
|
use ncc\Exceptions\FileNotFoundException;
|
||||||
use ncc\Exceptions\IOException;
|
use ncc\Exceptions\IOException;
|
||||||
use ncc\Exceptions\RuntimeException;
|
use ncc\Exceptions\RuntimeException;
|
||||||
use ncc\Objects\Vault;
|
use ncc\Objects\Vault;
|
||||||
|
@ -20,10 +20,16 @@
|
||||||
class CredentialManager
|
class CredentialManager
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var null
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $CredentialsPath;
|
private $CredentialsPath;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Vault
|
||||||
|
*/
|
||||||
|
private $Vault;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public Constructor
|
* Public Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -31,23 +37,16 @@
|
||||||
{
|
{
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
$this->CredentialsPath = PathFinder::getDataPath(Scopes::System) . DIRECTORY_SEPARATOR . 'credentials.store';
|
$this->CredentialsPath = PathFinder::getDataPath(Scopes::System) . DIRECTORY_SEPARATOR . 'credentials.store';
|
||||||
}
|
$this->Vault = null;
|
||||||
|
|
||||||
/**
|
try
|
||||||
* Determines if CredentialManager has correct access to manage credentials on the system
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function checkAccess(): bool
|
|
||||||
{
|
|
||||||
$ResolvedScope = Resolver::resolveScope();
|
|
||||||
|
|
||||||
if($ResolvedScope !== Scopes::System)
|
|
||||||
{
|
{
|
||||||
return False;
|
$this->loadVault();
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
unset($e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return True;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,95 +63,67 @@
|
||||||
if(file_exists($this->CredentialsPath))
|
if(file_exists($this->CredentialsPath))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!$this->checkAccess())
|
if(Resolver::resolveScope() !== Scopes::System)
|
||||||
{
|
|
||||||
throw new AccessDeniedException('Cannot construct credentials store without system permissions');
|
throw new AccessDeniedException('Cannot construct credentials store without system permissions');
|
||||||
}
|
|
||||||
|
|
||||||
$VaultObject = new Vault();
|
$VaultObject = new Vault();
|
||||||
$VaultObject->Version = Versions::CredentialsStoreVersion;
|
$VaultObject->Version = Versions::CredentialsStoreVersion;
|
||||||
|
|
||||||
IO::fwrite($this->CredentialsPath, ZiProto::encode($VaultObject->toArray()), 0600);
|
IO::fwrite($this->CredentialsPath, ZiProto::encode($VaultObject->toArray()), 0744);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the vault object from the credentials store file.
|
* Loads the vault from the disk
|
||||||
*
|
*
|
||||||
* @return Vault
|
|
||||||
* @throws AccessDeniedException
|
|
||||||
* @throws IOException
|
|
||||||
* @throws RuntimeException
|
|
||||||
*/
|
|
||||||
public function getVault(): Vault
|
|
||||||
{
|
|
||||||
$this->constructStore();
|
|
||||||
|
|
||||||
if(!$this->checkAccess())
|
|
||||||
{
|
|
||||||
throw new AccessDeniedException('Cannot read credentials store without system permissions');
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$Vault = ZiProto::decode(IO::fread($this->CredentialsPath));
|
|
||||||
}
|
|
||||||
catch(Exception $e)
|
|
||||||
{
|
|
||||||
// TODO: Implement error-correction for corrupted credentials store.
|
|
||||||
throw new RuntimeException($e->getMessage(), $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Vault::fromArray($Vault);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the vault object to the credentials store
|
|
||||||
*
|
|
||||||
* @param Vault $vault
|
|
||||||
* @return void
|
* @return void
|
||||||
* @throws AccessDeniedException
|
* @throws AccessDeniedException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
* @throws RuntimeException
|
||||||
|
* @throws FileNotFoundException
|
||||||
*/
|
*/
|
||||||
public function saveVault(Vault $vault): void
|
public function loadVault(): void
|
||||||
{
|
{
|
||||||
if(!$this->checkAccess())
|
if($this->Vault !== null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!file_exists($this->CredentialsPath))
|
||||||
{
|
{
|
||||||
throw new AccessDeniedException('Cannot write to credentials store without system permissions');
|
$this->Vault = new Vault();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IO::fwrite($this->CredentialsPath, ZiProto::encode($vault->toArray()), 0600);
|
$VaultArray = ZiProto::decode(IO::fread($this->CredentialsPath));
|
||||||
|
$VaultObject = new Vault();
|
||||||
|
$VaultObject->fromArray($VaultArray);
|
||||||
|
|
||||||
|
if($VaultObject->Version !== Versions::CredentialsStoreVersion)
|
||||||
|
throw new RuntimeException('Credentials store version mismatch');
|
||||||
|
|
||||||
|
$this->Vault = $VaultObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an entry to the credentials store file
|
* Saves the vault to the disk
|
||||||
*
|
*
|
||||||
* @param Vault\Entry $entry
|
|
||||||
* @return void
|
* @return void
|
||||||
* @throws AccessDeniedException
|
* @throws AccessDeniedException
|
||||||
* @throws InvalidCredentialsEntryException
|
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
* @noinspection PhpUnused
|
||||||
*/
|
*/
|
||||||
public function registerEntry(Vault\Entry $entry): void
|
public function saveVault(): void
|
||||||
{
|
{
|
||||||
if(!preg_match('/^[\w-]+$/', $entry->Alias))
|
if(Resolver::resolveScope() !== Scopes::System)
|
||||||
{
|
throw new AccessDeniedException('Cannot save credentials store without system permissions');
|
||||||
throw new InvalidCredentialsEntryException('The property \'Alias\' must be alphanumeric (Regex error)');
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement more validation checks for the rest of the entry properties.
|
IO::fwrite($this->CredentialsPath, ZiProto::encode($this->Vault->toArray()), 0744);
|
||||||
// TODO: Implement encryption for entries that require encryption (For securing passwords and data)
|
|
||||||
|
|
||||||
$Vault = $this->getVault();
|
|
||||||
$Vault->Entries[] = $entry;
|
|
||||||
|
|
||||||
$this->saveVault($Vault);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return null
|
* @return string
|
||||||
|
* @noinspection PhpUnused
|
||||||
*/
|
*/
|
||||||
public function getCredentialsPath(): ?string
|
public function getCredentialsPath(): string
|
||||||
{
|
{
|
||||||
return $this->CredentialsPath;
|
return $this->CredentialsPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpMissingFieldTypeInspection */
|
||||||
|
|
||||||
namespace ncc\Objects;
|
namespace ncc\Objects;
|
||||||
|
|
||||||
use ncc\Objects\Vault\DefaultEntry;
|
use ncc\Abstracts\AuthenticationType;
|
||||||
|
use ncc\Defuse\Crypto\Crypto;
|
||||||
|
use ncc\Exceptions\RuntimeException;
|
||||||
|
use ncc\Interfaces\PasswordInterface;
|
||||||
use ncc\Objects\Vault\Entry;
|
use ncc\Objects\Vault\Entry;
|
||||||
|
use ncc\Utilities\Functions;
|
||||||
|
use ncc\ZiProto\ZiProto;
|
||||||
|
|
||||||
class Vault
|
class Vault
|
||||||
{
|
{
|
||||||
|
@ -21,13 +28,6 @@
|
||||||
*/
|
*/
|
||||||
public $Entries;
|
public $Entries;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @var DefaultEntry[]
|
|
||||||
*/
|
|
||||||
public $DefaultEntries;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public Constructor
|
* Public Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -37,46 +37,168 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array representation of the object
|
* Adds a new entry to the vault
|
||||||
*
|
*
|
||||||
* @return array
|
* @param string $name
|
||||||
|
* @param PasswordInterface $password
|
||||||
|
* @param bool $encrypt
|
||||||
|
* @return bool
|
||||||
|
* @noinspection PhpUnused
|
||||||
*/
|
*/
|
||||||
public function toArray(): array
|
public function addEntry(string $name, PasswordInterface $password, bool $encrypt=true): bool
|
||||||
{
|
{
|
||||||
$Entries = [];
|
// Check if the entry already exists
|
||||||
|
|
||||||
foreach($this->Entries as $entry)
|
foreach($this->Entries as $entry)
|
||||||
{
|
{
|
||||||
$Entries[] = $entry->toArray();
|
if($entry->getName() === $name)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new entry
|
||||||
|
$entry = new Entry();
|
||||||
|
$entry->setName($name);
|
||||||
|
$entry->setEncrypted($encrypt);
|
||||||
|
$entry->setAuthentication($password);
|
||||||
|
|
||||||
|
// Add the entry to the vault
|
||||||
|
$this->Entries[] = $entry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an entry from the vault
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function deleteEntry(string $name): bool
|
||||||
|
{
|
||||||
|
foreach($this->Entries as $entry)
|
||||||
|
{
|
||||||
|
if($entry->getName() === $name)
|
||||||
|
{
|
||||||
|
$this->Entries = array_diff($this->Entries, [$entry]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the entries in the vault
|
||||||
|
*
|
||||||
|
* @return array|Entry[]
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function getEntries(): array
|
||||||
|
{
|
||||||
|
return $this->Entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an existing entry from the vault
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return Entry|null
|
||||||
|
*/
|
||||||
|
public function getEntry(string $name): ?Entry
|
||||||
|
{
|
||||||
|
foreach($this->Entries as $entry)
|
||||||
|
{
|
||||||
|
if($entry->getName() === $name)
|
||||||
|
return $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates an entry in the vault
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $password
|
||||||
|
* @return bool
|
||||||
|
* @throws RuntimeException
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function authenticate(string $name, string $password): bool
|
||||||
|
{
|
||||||
|
$entry = $this->getEntry($name);
|
||||||
|
if($entry === null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($entry->getPassword() === null)
|
||||||
|
{
|
||||||
|
if($entry->isEncrypted() && !$entry->isIsCurrentlyDecrypted())
|
||||||
|
{
|
||||||
|
return $entry->unlock($password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$input = [];
|
||||||
|
switch($entry->getPassword()->getAuthenticationType())
|
||||||
|
{
|
||||||
|
case AuthenticationType::UsernamePassword:
|
||||||
|
$input = ['password' => $password];
|
||||||
|
break;
|
||||||
|
case AuthenticationType::AccessToken:
|
||||||
|
$input = ['token' => $password];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entry->authenticate($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array representation of the object
|
||||||
|
*
|
||||||
|
* @param bool $bytecode
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(bool $bytecode=false): array
|
||||||
|
{
|
||||||
|
$entries = [];
|
||||||
|
foreach($this->Entries as $entry)
|
||||||
|
{
|
||||||
|
$entry_array = $entry->toArray($bytecode);
|
||||||
|
|
||||||
|
if($entry->getPassword() !== null && $entry->isEncrypted())
|
||||||
|
{
|
||||||
|
$entry_array['password'] = Crypto::encryptWithPassword(
|
||||||
|
ZiProto::encode($entry_array['password']), $entry->getPassword()->__toString(), $bytecode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries[] = $entry_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'version' => $this->Version,
|
($bytecode ? Functions::cbc('version') : 'version') => $this->Version,
|
||||||
'entries' => $Entries
|
($bytecode ? Functions::cbc('entries') : 'entries') => $entries,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an object from an array representation
|
* Constructs a new object from an array
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $array
|
||||||
* @return Vault
|
* @return Vault
|
||||||
*/
|
*/
|
||||||
public static function fromArray(array $data): Vault
|
public static function fromArray(array $array): Vault
|
||||||
{
|
{
|
||||||
$VaultObject = new Vault();
|
$vault = new Vault();
|
||||||
|
$vault->Version = Functions::array_bc($array, 'version');
|
||||||
|
$entries = Functions::array_bc($array, 'entries');
|
||||||
|
|
||||||
if(isset($data['version']))
|
foreach($entries as $entry)
|
||||||
$VaultObject->Version = $data['version'];
|
|
||||||
|
|
||||||
if(isset($data['entries']))
|
|
||||||
{
|
{
|
||||||
foreach($data['entries'] as $entry)
|
$entry = Entry::fromArray($entry);
|
||||||
{
|
$vault->Entries[] = $entry;
|
||||||
$VaultObject->Entries[] = Entry::fromArray($entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $VaultObject;
|
return $vault;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
/** @noinspection PhpMissingFieldTypeInspection */
|
|
||||||
|
|
||||||
namespace ncc\Objects\Vault;
|
|
||||||
|
|
||||||
class DefaultEntry
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The alias entry to use for default authentication
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $Alias;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source that the alias is for
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $Source;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array representation of the object
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function toArray(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'alias' => $this->Alias,
|
|
||||||
'source' => $this->Source
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the object from an array representation
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return DefaultEntry
|
|
||||||
*/
|
|
||||||
public static function fromArray(array $data): DefaultEntry
|
|
||||||
{
|
|
||||||
$DefaultEntryObject = new DefaultEntry();
|
|
||||||
|
|
||||||
if(isset($data['alias']))
|
|
||||||
{
|
|
||||||
$DefaultEntryObject->Alias = $data['alias'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($data['source']))
|
|
||||||
{
|
|
||||||
$DefaultEntryObject->Source = $data['source'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $DefaultEntryObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,124 +4,327 @@
|
||||||
|
|
||||||
namespace ncc\Objects\Vault;
|
namespace ncc\Objects\Vault;
|
||||||
|
|
||||||
use ncc\Abstracts\RemoteAuthenticationType;
|
use ncc\Abstracts\AuthenticationType;
|
||||||
use ncc\Abstracts\RemoteSource;
|
use ncc\Defuse\Crypto\Crypto;
|
||||||
|
use ncc\Defuse\Crypto\Exception\EnvironmentIsBrokenException;
|
||||||
|
use ncc\Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
|
||||||
|
use ncc\Exceptions\RuntimeException;
|
||||||
|
use ncc\Interfaces\PasswordInterface;
|
||||||
|
use ncc\Objects\Vault\Password\AccessToken;
|
||||||
|
use ncc\Objects\Vault\Password\UsernamePassword;
|
||||||
|
use ncc\Utilities\Functions;
|
||||||
|
use ncc\ZiProto\ZiProto;
|
||||||
|
|
||||||
class Entry
|
class Entry
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The unique alias of the source entry, can also be used for remote resource fetching for dependencies with the
|
* The entry's unique identifier
|
||||||
* following example schemes;
|
|
||||||
*
|
|
||||||
* - alias@github.com/org/package
|
|
||||||
* - alias@git.example.org/org/package
|
|
||||||
* - alias@gitlab.com/org/package
|
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $Alias;
|
private $Name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The remote source of the entry, currently only supported sources are allowed.
|
* Whether the entry's password is encrypted
|
||||||
*
|
|
||||||
* @var string|RemoteSource
|
|
||||||
*/
|
|
||||||
public $Source;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The host of the remote source, eg; github.com or git.example.org, will be used for remote resource fetching
|
|
||||||
* for dependencies with the following example schemes;
|
|
||||||
*
|
|
||||||
* - github.com/org/package
|
|
||||||
* - git.example.org/org/package
|
|
||||||
* - gitlab.com/org/package
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $SourceHost;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|RemoteAuthenticationType
|
|
||||||
*/
|
|
||||||
public $AuthenticationType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the authentication details are encrypted or not, if encrypted a passphrase is required
|
|
||||||
* by the user
|
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $Encrypted;
|
private $Encrypted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The authentication details.
|
* The entry's password
|
||||||
*
|
*
|
||||||
* If the remote authentication type is private access token, the first index (0) would be the key itself
|
* @var PasswordInterface|string|null
|
||||||
* If the remote authentication type is a username and password, first index would be Username and second
|
|
||||||
* would be the password.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
public $Authentication;
|
private $Password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the entry's password is currently decrypted in memory
|
||||||
|
* (Not serialized)
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $IsCurrentlyDecrypted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array representation of the object
|
* Returns an array representation of the object
|
||||||
*
|
*
|
||||||
* @return array
|
|
||||||
* @noinspection PhpArrayShapeAttributeCanBeAddedInspection
|
|
||||||
*/
|
*/
|
||||||
public function toArray(): array
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
$this->Encrypted = true;
|
||||||
|
$this->IsCurrentlyDecrypted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Authenticates the entry
|
||||||
|
*
|
||||||
|
* For UsernamePassword the $input parameter expects an array with the keys 'username' and 'password'
|
||||||
|
* For AccessToken the $input parameter expects an array with the key 'token'
|
||||||
|
*
|
||||||
|
* @param array $input
|
||||||
|
* @return bool
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function authenticate(array $input): bool
|
||||||
|
{
|
||||||
|
if(!$this->IsCurrentlyDecrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($this->Password == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch($this->Password->getAuthenticationType())
|
||||||
|
{
|
||||||
|
case AuthenticationType::UsernamePassword:
|
||||||
|
if(!($this->Password instanceof UsernamePassword))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$username = $input['username'] ?? null;
|
||||||
|
$password = $input['password'] ?? null;
|
||||||
|
|
||||||
|
if($username === null && $password === null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($username == null)
|
||||||
|
return $password == $this->Password->Password;
|
||||||
|
|
||||||
|
if($password == null)
|
||||||
|
return $username == $this->Password->Username;
|
||||||
|
|
||||||
|
return $username == $this->Password->Username && $password == $this->Password->Password;
|
||||||
|
|
||||||
|
case AuthenticationType::AccessToken:
|
||||||
|
if(!($this->Password instanceof AccessToken))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$token = $input['token'] ?? null;
|
||||||
|
|
||||||
|
if($token === null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $token == $this->Password->AccessToken;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PasswordInterface $password
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setAuthentication(PasswordInterface $password): void
|
||||||
|
{
|
||||||
|
$this->Password = $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function isIsCurrentlyDecrypted(): bool
|
||||||
|
{
|
||||||
|
return $this->IsCurrentlyDecrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks the entry by encrypting the password
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function lock(): bool
|
||||||
|
{
|
||||||
|
if($this->Password == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($this->Encrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!$this->IsCurrentlyDecrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!($this->Password instanceof PasswordInterface))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->Password = $this->encrypt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks the entry by decrypting the password
|
||||||
|
*
|
||||||
|
* @param string $password
|
||||||
|
* @return bool
|
||||||
|
* @throws RuntimeException
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function unlock(string $password): bool
|
||||||
|
{
|
||||||
|
if($this->Password == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!$this->Encrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($this->IsCurrentlyDecrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!is_string($this->Password))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$password = Crypto::decryptWithPassword($this->Password, $password, true);
|
||||||
|
}
|
||||||
|
catch (EnvironmentIsBrokenException $e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException('Cannot decrypt password', $e);
|
||||||
|
}
|
||||||
|
catch (WrongKeyOrModifiedCiphertextException $e)
|
||||||
|
{
|
||||||
|
unset($e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->Password = ZiProto::decode($password);
|
||||||
|
$this->IsCurrentlyDecrypted = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the password object as an encrypted binary string
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private function encrypt(): ?string
|
||||||
|
{
|
||||||
|
if(!$this->IsCurrentlyDecrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($this->Password == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!($this->Password instanceof PasswordInterface))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$password = ZiProto::encode($this->Password->toArray(true));
|
||||||
|
return Crypto::encryptWithPassword($password, $password, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array representation of the object
|
||||||
|
*
|
||||||
|
* @param bool $bytecode
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(bool $bytecode=false): array
|
||||||
|
{
|
||||||
|
if(!$this->Password)
|
||||||
|
{
|
||||||
|
if($this->Encrypted && $this->IsCurrentlyDecrypted)
|
||||||
|
{
|
||||||
|
$password = $this->encrypt();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$password = $this->Password->toArray(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$password = $this->Password;
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'alias' => $this->Alias,
|
($bytecode ? Functions::cbc('name') : 'name') => $this->Name,
|
||||||
'source' => $this->Source,
|
($bytecode ? Functions::cbc('encrypted') : 'encrypted') => $this->Encrypted,
|
||||||
'source_host' => $this->SourceHost,
|
($bytecode ? Functions::cbc('password') : 'password') => $password,
|
||||||
'authentication_type' => $this->AuthenticationType,
|
|
||||||
'encrypted' => $this->Encrypted,
|
|
||||||
'authentication' => $this->Authentication
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array representation of the object
|
* Constructs an object from an array representation
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @return Entry
|
* @return Entry
|
||||||
*/
|
*/
|
||||||
public static function fromArray(array $data): Entry
|
public static function fromArray(array $data): self
|
||||||
{
|
{
|
||||||
$EntryObject = new Entry();
|
$self = new self();
|
||||||
|
|
||||||
if(isset($data['alias']))
|
$self->Name = Functions::array_bc($data, 'name');
|
||||||
|
$self->Encrypted = Functions::array_bc($data, 'encrypted');
|
||||||
|
|
||||||
|
$password = Functions::array_bc($data, 'password');
|
||||||
|
if($password !== null)
|
||||||
{
|
{
|
||||||
$EntryObject->Alias = $data['alias'];
|
if($self->Encrypted)
|
||||||
|
{
|
||||||
|
$self->Password = $password;
|
||||||
|
$self->IsCurrentlyDecrypted = false;
|
||||||
|
}
|
||||||
|
elseif(gettype($password) == 'array')
|
||||||
|
{
|
||||||
|
$self->Password = match (Functions::array_bc($data, 'authentication_type')) {
|
||||||
|
AuthenticationType::UsernamePassword => UsernamePassword::fromArray($password),
|
||||||
|
AuthenticationType::AccessToken => AccessToken::fromArray($password)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($data['source']))
|
return $self;
|
||||||
{
|
}
|
||||||
$EntryObject->Source = $data['source'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($data['source_host']))
|
/**
|
||||||
{
|
* @return bool
|
||||||
$EntryObject->SourceHost = $data['source_host'];
|
*/
|
||||||
}
|
public function isEncrypted(): bool
|
||||||
|
{
|
||||||
|
return $this->Encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
if(isset($data['authentication_type']))
|
/**
|
||||||
{
|
* Returns false if the entry needs to be decrypted first
|
||||||
$EntryObject->AuthenticationType = $data['authentication_type'];
|
*
|
||||||
}
|
* @param bool $Encrypted
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setEncrypted(bool $Encrypted): bool
|
||||||
|
{
|
||||||
|
if(!$this->IsCurrentlyDecrypted)
|
||||||
|
return false;
|
||||||
|
|
||||||
if(isset($data['encrypted']))
|
$this->Encrypted = $Encrypted;
|
||||||
{
|
return true;
|
||||||
$EntryObject->Encrypted = $data['encrypted'];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($data['authentication']))
|
/**
|
||||||
{
|
* @return string
|
||||||
$EntryObject->Authentication = $data['authentication'];
|
*/
|
||||||
}
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->Name;
|
||||||
|
}
|
||||||
|
|
||||||
return $EntryObject;
|
/**
|
||||||
|
* @param string $Name
|
||||||
|
*/
|
||||||
|
public function setName(string $Name): void
|
||||||
|
{
|
||||||
|
$this->Name = $Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PasswordInterface|null
|
||||||
|
*/
|
||||||
|
public function getPassword(): ?PasswordInterface
|
||||||
|
{
|
||||||
|
if(!$this->IsCurrentlyDecrypted)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $this->Password;
|
||||||
}
|
}
|
||||||
}
|
}
|
74
src/ncc/Objects/Vault/Password/AccessToken.php
Normal file
74
src/ncc/Objects/Vault/Password/AccessToken.php
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpMissingFieldTypeInspection */
|
||||||
|
|
||||||
|
namespace ncc\Objects\Vault\Password;
|
||||||
|
|
||||||
|
use ncc\Abstracts\AuthenticationType;
|
||||||
|
use ncc\Interfaces\PasswordInterface;
|
||||||
|
use ncc\Utilities\Functions;
|
||||||
|
|
||||||
|
class AccessToken implements PasswordInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The entry's access token
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $AccessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array representation of the object
|
||||||
|
*
|
||||||
|
* @param bool $bytecode
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(bool $bytecode=false): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
($bytecode ? Functions::cbc('authentication_type') : 'authentication_type') => AuthenticationType::AccessToken,
|
||||||
|
($bytecode ? Functions::cbc('access_token') : 'access_token') => $this->AccessToken,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an object from an array representation
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $data): self
|
||||||
|
{
|
||||||
|
$object = new self();
|
||||||
|
|
||||||
|
$object->AccessToken = Functions::array_bc($data, 'access_token');
|
||||||
|
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAccessToken(): string
|
||||||
|
{
|
||||||
|
return $this->AccessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getAuthenticationType(): string
|
||||||
|
{
|
||||||
|
return AuthenticationType::AccessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->AccessToken;
|
||||||
|
}
|
||||||
|
}
|
93
src/ncc/Objects/Vault/Password/UsernamePassword.php
Normal file
93
src/ncc/Objects/Vault/Password/UsernamePassword.php
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpMissingFieldTypeInspection */
|
||||||
|
|
||||||
|
namespace ncc\Objects\Vault\Password;
|
||||||
|
|
||||||
|
use ncc\Abstracts\AuthenticationType;
|
||||||
|
use ncc\Interfaces\PasswordInterface;
|
||||||
|
use ncc\Utilities\Functions;
|
||||||
|
|
||||||
|
class UsernamePassword implements PasswordInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The entry's username
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $Username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry's password
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $Password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array representation of the object
|
||||||
|
*
|
||||||
|
* @param bool $bytecode
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray(bool $bytecode=false): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
($bytecode ? Functions::cbc('authentication_type') : 'authentication_type') => AuthenticationType::UsernamePassword,
|
||||||
|
($bytecode ? Functions::cbc('username') : 'username') => $this->Username,
|
||||||
|
($bytecode ? Functions::cbc('password') : 'password') => $this->Password,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an object from an array representation
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $data): self
|
||||||
|
{
|
||||||
|
$instance = new self();
|
||||||
|
|
||||||
|
$instance->Username = Functions::array_bc($data, 'username');
|
||||||
|
$instance->Password = Functions::array_bc($data, 'password');
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->Username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @noinspection PhpUnused
|
||||||
|
*/
|
||||||
|
public function getPassword(): string
|
||||||
|
{
|
||||||
|
return $this->Password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getAuthenticationType(): string
|
||||||
|
{
|
||||||
|
return AuthenticationType::UsernamePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->Password;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue