From a4199fc27de1aec951af97d6cc5c13ff80337fe1 Mon Sep 17 00:00:00 2001 From: maillotf Date: Wed, 30 Sep 2020 16:02:27 +0200 Subject: [PATCH] Initial commit --- README.md | 60 +++ composer.json | 27 ++ src/DependencyInjection/Configuration.php | 56 +++ src/DependencyInjection/PapercutExtension.php | 37 ++ src/Exception/PapercutException.php | 12 + src/Objects/AbstractPapercutObject.php | 19 + src/Objects/User.php | 122 +++++ src/PapercutBridgeBundle.php | 21 + src/Resources/config/services.yml | 9 + src/Service/AbstractPapercut.php | 64 +++ src/Service/PapercutService.php | 41 ++ src/Service/PapercutServiceInterface.php | 12 + src/Service/UserService.php | 423 ++++++++++++++++++ src/Service/UserServiceInterface.php | 53 +++ src/Utils/Normalizer.php | 45 ++ 15 files changed, 1001 insertions(+) create mode 100644 README.md create mode 100644 composer.json create mode 100644 src/DependencyInjection/Configuration.php create mode 100644 src/DependencyInjection/PapercutExtension.php create mode 100644 src/Exception/PapercutException.php create mode 100644 src/Objects/AbstractPapercutObject.php create mode 100644 src/Objects/User.php create mode 100644 src/PapercutBridgeBundle.php create mode 100644 src/Resources/config/services.yml create mode 100644 src/Service/AbstractPapercut.php create mode 100644 src/Service/PapercutService.php create mode 100644 src/Service/PapercutServiceInterface.php create mode 100644 src/Service/UserService.php create mode 100644 src/Service/UserServiceInterface.php create mode 100644 src/Utils/Normalizer.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1dc347 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Papercut-bridge-bundle + +Symfony bundle for Papercut XML-RPC client which is base on token authentication + +## Required configuration + +### Modify framework.yaml +```yaml +papercut: + authentication: + protocol: "http" + host: "127.0.0.1" + port: "80" + path: "/rpc/api/xmlrpc" + token: "TOKEN" +``` + +```yaml +papercut: + authentication: + path: "http://URL/rpc/api/xmlrpc" + token: "TOKEN" +``` + +### Modify services.yaml +```yaml +services: + MaillotF\Papercut\PapercutBridgeBundle\Service\PapercutService: '@papercut.service' +``` + +##Package instalation with composer + +```console +$ composer require maillotf/papercut-bridge-bundle +``` + +## Use in controller: + +```php +user->getUser('4665'); + + return ($this->json($user->getEmail())); + } + +} +``` \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..37ecbb1 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ + +{ + "name": "maillotf/papercut-bridge-bundle", + "type": "symfony-bundle", + "description": "A Papercut bundle for Symfony", + "keywords": ["papercut", "print", "bridge", "symfony"], + "homepage": "https://github.com/maillotf/papercut-bridge-bundle", + "authors": [{ + "name": "Flavien Maillot", + "email": "contact@webcomputing.fr" + }], + "require": { + "php": "^7.1", + "symfony/http-kernel": "^3.2|^4.0|^4.1|^5.1", + "symfony/config": "^3.2|^4.0|^4.1|^5.1", + "symfony/dependency-injection": "^3.2|^4.0|^4.1|^5.1", + "phpxmlrpc/phpxmlrpc": "^4.3" + }, + "require-dev": { + "phpunit/phpunit": "^6.5" + }, + "autoload": { + "psr-4": { + "MaillotF\\Papercut\\PapercutBridgeBundle\\": "src/" + } + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 0000000..486e051 --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,56 @@ +getRootNode()->addDefaultsIfNotSet() + ->children() + ->arrayNode('authentication') + ->isRequired() + ->children() + ->scalarNode('protocol') +// ->isRequired() +// ->cannotBeEmpty() + ->defaultValue(null) + ->end() + ->scalarNode('host') +// ->isRequired() +// ->cannotBeEmpty() + ->defaultValue(null) + ->end() + ->integerNode('port') +// ->isRequired() + ->defaultValue(null) + ->end() + ->scalarNode('path') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->scalarNode('token') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->end() + ->end() + ->end() + ; + return ($builder); + } +} diff --git a/src/DependencyInjection/PapercutExtension.php b/src/DependencyInjection/PapercutExtension.php new file mode 100644 index 0000000..4fd0830 --- /dev/null +++ b/src/DependencyInjection/PapercutExtension.php @@ -0,0 +1,37 @@ +processConfiguration($configuration, $configs); + + // Authentication + $container->setParameter('papercut.authentication.protocol', $config['authentication']['protocol']); + $container->setParameter('papercut.authentication.host', $config['authentication']['host']); + $container->setParameter('papercut.authentication.port', $config['authentication']['port']); + $container->setParameter('papercut.authentication.path', $config['authentication']['path']); + $container->setParameter('papercut.authentication.token', $config['authentication']['token']); + + // load services for bundle + $loader = new YamlFileLoader( + $container, + new FileLocator(__DIR__ . '/../Resources/config') + ); + $loader->load('services.yml'); + } +} diff --git a/src/Exception/PapercutException.php b/src/Exception/PapercutException.php new file mode 100644 index 0000000..d7e62c5 --- /dev/null +++ b/src/Exception/PapercutException.php @@ -0,0 +1,12 @@ +username = $username; + return $this; + } + + public function getUsername(): string + { + return $this->username; + } + + public function setPassword(string $password) + { + $this->password = $password; + return $this; + } + + public function getPassword(): string + { + return $this->password; + } + + public function setFullname(string $fullName) + { + $this->fullName = $fullName; + return $this; + } + + public function getFullname(): string + { + return $this->fullName; + } + + public function setEmail(string $email) + { + $this->email = $email; + return $this; + } + + public function getEmail(): string + { + return $this->email; + } + + public function setBalance(string $balance) + { + $this->balance = $balance; + return $this; + } + + public function getBalance(): string + { + return $this->balance; + } + + public function setPrimaryCardNumber(string $primaryCardNumber) + { + $this->primaryCardNumber = $primaryCardNumber; + return $this; + } + public function getPrimaryCardNumber(): string + { + return $this->primaryCardNumber; + } + + public function setSecondaryCardNumber(string $secondaryCardNumber) + { + $this->secondaryCardNumber = $secondaryCardNumber; + return $this; + } + public function getSecondaryCardNumber(): string + { + return $this->secondaryCardNumber; + } + + public function setRestricted(string $restricted) + { + $this->restricted = $restricted; + return $this; + } + + public function getRestricted(): string + { + return $this->restricted; + } + +} diff --git a/src/PapercutBridgeBundle.php b/src/PapercutBridgeBundle.php new file mode 100644 index 0000000..7385d46 --- /dev/null +++ b/src/PapercutBridgeBundle.php @@ -0,0 +1,21 @@ +token = $token; + if ($protocol != null && $host != null && $port != null) + $this->client = new PhpXmlRpc\Client($path, $host, $port, $protocol); + else + $this->client = new PhpXmlRpc\Client($path); + } + + /** + * Request to the papercut server + * + * @param string $name + * @param array $data + * @return string|array + * @author Flavien Maillot + */ + protected function request(string $name, array $data) + { + array_unshift($data, new PhpXmlRpc\Value($this->token, 'string')); + $message = new PhpXmlRpc\Request('api.' . $name, $data); + $response = $this->client->send($message); + + if ($response->faultCode()) + { + return 'ERROR: ' . $response->faultString(); + } + else + { + return $response->value()->scalarval(); + } + } + +} diff --git a/src/Service/PapercutService.php b/src/Service/PapercutService.php new file mode 100644 index 0000000..8ad6ddc --- /dev/null +++ b/src/Service/PapercutService.php @@ -0,0 +1,41 @@ +user = new UserService($path, $token, $protocol, $host, $port); + } + + /** + * + * @return \MaillotF\Papercut\PapercutBridgeBundle\Service\UserService + * @author Flavien Maillot + */ + public function getUserService(): UserService + { + return ($this->user); + } + +} diff --git a/src/Service/PapercutServiceInterface.php b/src/Service/PapercutServiceInterface.php new file mode 100644 index 0000000..cd7ea96 --- /dev/null +++ b/src/Service/PapercutServiceInterface.php @@ -0,0 +1,12 @@ +request('getUserProperty', array( + new PhpXmlRpc\Value($username, 'string'), + new PhpXmlRpc\Value($propertyName, 'string') + )); + return ($result); + } + + /** + * Save an user property + * + * @param string $propertyName + * @param string $username + * @param mixed $value + * @return bool success or fail + * @author Flavien Maillot + */ + public function saveProperty(string $propertyName, string $username, $value): bool + { + return $this->request('setUserProperty', array( + new PhpXmlRpc\Value($username, 'string'), + new PhpXmlRpc\Value($propertyName, 'string'), + new PhpXmlRpc\Value($value, 'string'), + )); + } + + /** + * Save an user + * + * @param User $user + * @return bool success or fail + */ + public function saveUser(User $user): bool + { + $userProperties = $user->getProperties(); + $xmlProperties = array(); + foreach ($userProperties as $userProperty) + { + $getter = $userProperty['getter']; + $value = $user->$getter(); + $property = array( + new PhpXmlRpc\Value($userProperty['property'], 'string'), + new PhpXmlRpc\Value($value, 'string') + ); + $xmlProperties[] = new PhpXmlRpc\Value($property, 'array'); + } + if ($user->getPassword() != null && $user->getPassword() != '') + { + $property = array( + new PhpXmlRpc\Value('password', 'string'), + new PhpXmlRpc\Value($user->getPassword(), 'string') + ); + $xmlProperties[] = new PhpXmlRpc\Value($property, 'array'); + } + + return $this->request('setUserProperties', array( + new PhpXmlRpc\Value($user->getUsername(), 'string'), + new PhpXmlRpc\Value($xmlProperties, 'array') + )); + } + + /** + * Get an user + * + * @param string $username + * @return User + * @author Flavien Maillot + */ + public function getUser(string $username): User + { + $user = new User(); + $user->setUsername($username); + $userProperties = $user->getProperties(); + $xmlProperties = array(); + foreach ($userProperties as $userProperty) + { + $xmlProperties[] = new PhpXmlRpc\Value($userProperty['property'], 'string'); + } + + $properties = $this->request('getUserProperties', array( + new PhpXmlRpc\Value($username, 'string'), + new PhpXmlRpc\Value($xmlProperties, 'array') + )); + + foreach ($properties as $value) + { + $setter = current($userProperties)['setter']; + $user->$setter($value['string']); + next($userProperties); + } + + return $user; + } + + /** + * Get a username by the card number + * + * @param string $revhex + * @return string + * @author Flavien Maillot + */ + public function getUsernameByCardNo(string $revhex): string + { + $username = $this->request('lookUpUserNameByCardNo', array( + new PhpXmlRpc\Value($revhex, 'string'))); + return ($username); + } + + /** + * Get all username in a list + * + * @return array + * @throws \RuntimeException + * @author Flavien Maillot + */ + public function getUsernameList(): array + { + $result = $this->request('listUserAccounts', array(new PhpXmlRpc\Value(0, 'int'), new PhpXmlRpc\Value(0, 'int'))); + + $usersList = array(); + if (!is_array($result)) + throw new \RuntimeException($result); + foreach ($result as $data) + $usersList[] = $data['string']; + + return ($usersList); + } + + /** + * Check if user existe + * + * @param string $username + * @return bool + * @author Flavien Maillot + */ + public function isUserExists(string $username): bool + { + $result = $this->request('isUserExists', array( + new PhpXmlRpc\Value($username, 'string') + )); + + return $result; + } + + /** + * Adjust $amount to the $username papercut balance + * + * @param string $username + * @param float $amount + * @param string $label + * @return bool + * @author Flavien Maillot + */ + public function adjustBalance(string $username, float $amount, string $label = 'Adjust balance by Papercut bridge'): bool + { + return ($this->request('adjustUserAccountBalance', array( + new PhpXmlRpc\Value($username, 'string'), + new PhpXmlRpc\Value($amount, 'double'), + new PhpXmlRpc\Value($label, 'string'), + ))); + } + + /** + * Get User balance + * + * @param string $username + * @return string + * @author Flavien Maillot + */ + public function getBalance(string $username): float + { + $balance = $this->request('getUserAccountBalance', array( + new PhpXmlRpc\Value($username, 'string') + )); + + return (floatval($balance)); + } + + /** + * Get User account balance rounded + * + * @param string $username + * @return string + * @author Flavien Maillot + */ + public function getBalanceRounded(string $username): float + { + return (round($this->getBalance($username), 2)); + } + + /** + * Delete user account + * + * @param string $username + * @return bool + * @author Flavien Maillot + */ + public function deleteUser(string $username): bool + { + if ($this->request('deleteExistingUser', array( + new PhpXmlRpc\Value($username, 'string') + )) === true) + return true; + return false; + } + + /** + * Create user from User object. + * Only use username, password, fullname, email, primaryCardNumber + * + * @param User $user + * @return bool + */ + public function createUser(User $user): bool + { + return ($this->createUserByParams($user->getUsername(), $user->getPassword(), $user->getFullname(), $user->getEmail(), $user->getPrimaryCardNumber())); + } + + /** + * Create user account with card + * + * @param array $params + * @return bool + * @author Flavien Maillot + */ + public function createUserByParams(string $username, string $password, string $fullname, string $email, string $card): bool + { + if (strpos($ret = $this->request('addNewInternalUser', array( + new PhpXmlRpc\Value($username, 'string'), + new PhpXmlRpc\Value($password, 'string'), + new PhpXmlRpc\Value($fullname, 'string'), + new PhpXmlRpc\Value($email, 'string'), + new PhpXmlRpc\Value($card, 'string'), + new PhpXmlRpc\Value('', 'string') + )), 'ERROR') === 0) + return false; + else + return true; + } + + /** + * Create user and handback previous card carrier if necessary + * + * @param User $user + * @return bool + * @author Flavien Maillot + */ + public function createUserWithHandback(User $user): bool + { + //Fail if card is linked to another contact + if (!$this->createUser($user)) + { + //get previous carrier or return false for error + if (empty($previousCarrier = $this->getUsernameByCardNo($user->getPrimaryCardNumber()))) + return (false); + else + { + //Try to handback previous carrier card or return false for error + if (($change = $this->saveProperty('primary-card-number', $previousCarrier, "OLD_${previousCarrier}_${card}")) !== true) + return (false); + //Try again to create the user or return false for error + if (!$this->createUser($user)) + return (false); + } + } + return (true); + } + + /** + * Create a papercut user if it doesn't exist + * + * @param string $username + * @param string $password + * @param string $fullname + * @param string $email + * @param string $card + * @return bool + * @author Flavien Maillot + */ + public function createUserIfDoesntExist(User $user): bool + { + if ($this->isUserExists($user->getUsername()) !== true) + { + if ($this->createUserWithHandback($user) == false) + return (false); + if (boolval($user->getRestricted()) == false) + $this->saveProperty('restricted', $user->getUsername(), $user->getRestricted()); + if ($user->getSecondaryCardNumber() == '') + $this->saveProperty('secondary-card-number', $user->getUsername(), $user->getEmail()); + else + $this->saveProperty('secondary-card-number', $user->getUsername(), $user->getSecondaryCardNumber()); + return (true); + } + else + return (false); + } + + /** + * Get total user registered + * + * @return int + * @author Flavien Maillot + */ + public function countTotalUsers(): int + { + return $this->request('getTotalUsers', array()); + } + + /** + * Helper to change the password for an user + * + * @param string $username + * @param string $password + * @return bool + * @author Flavien Maillot + */ + public function changePassword(string $username, string $password): bool + { + $result = $this->saveProperty('password', $username, $password); + if ($result === true) + return (true); + return (false); + } + + /** + * Change the primary primary card for a user + * + * @param string $username + * @param string $revhex + * @return array + * @author Flavien Maillot + */ + public function changePrimaryCardNumber(string $username, string $revhex, bool $strict = false): array + { + $result = $this->saveProperty('primary-card-number', $username, $revhex); + $matches = array(); + if ($result === true) + return true; + else if (preg_match('/.* number for user .* Card number: (.*)/', $result, $matches) == true && $strict == true) + throw new PapercutException('Unique constrainte', 409); + if ($strict == true) + throw new PapercutException($result, 500); + } + + /** + * Change the primary primary card for a user + * + * @param string $username + * @param string $revhex + * @param bool $strict + * @return array + * @throws PapercutException + * @author Flavien Maillot + */ + public function changeSecondaryCardNumber(string $username, string $revhex, bool $strict = false): array + { + $result = $this->saveProperty('secondary-card-number', $username, $revhex); + $matches = array(); + if ($result === true) + return true; + else if (preg_match('/.* number for user .* Card number: (.*)/', $result, $matches) == true && $strict == true) + throw new PapercutException('Unique constrainte', 409); + if ($strict == true) + throw new PapercutException($result, 500); + } + + /** + * Handback the card if it was link to another user, then link the primary card to the new user + * + * @param string $username + * @param string $revhex + * @param type $strict + * @return array + * @author Flavien Maillot + */ + public function handbackAndChangeUserPrimaryCardNumber(string $username, string $revhex, $strict = false): array + { + if (!empty($previousOwner = $this->getUsernameByCardNo($revhex))) + { + $result = $this->changePrimaryCardNumber($previousOwner, "OLD_${previousOwner}_${revhex}", $strict); + if ($result !== true) + return (false); + } + return ($this->changePrimaryCardNumber($username, $revhex, $strict)); + } + +} diff --git a/src/Service/UserServiceInterface.php b/src/Service/UserServiceInterface.php new file mode 100644 index 0000000..df202b0 --- /dev/null +++ b/src/Service/UserServiceInterface.php @@ -0,0 +1,53 @@ + $value) + { + $values[$value] = array( + 'setter' => 'set'.ucfirst($value), + 'getter' => 'get'.ucfirst($value), + 'property' => str_replace(self::$alphaSearch, self::$alphaReplace, $value) + ); + unset($values[$key]); + } + return ($values); + } +}