0 */ private $id = null; /** Id of the user * * @var int > 0 */ private $userId = null; /** base62 coded key * * @var string */ private $key = null; /** what you can do with this key * * @var array(string) */ private $rights = null; /** until when the key is valid * * @var DateTime */ private $endDate = null; /** Constructor * * sets all the members: * - converts the params to the internal type * - provides input sanitation * * @param mixed $id unique identifier of the apiKey * @param mixed $userId $id of the user the apiKey belongs to * @param mixed $key key identifier/representation * @param mixed $rights set of rights describing what the key is valid for * @param mixed $endDate the last day the key will be valid */ public function __construct($id, $userId, $key, $rights, $endDate) { $this->id = filterId($id); $this->userId = filterId($userId); $this->key = self::isWellFormatted($key) ? $key : null; $this->rights = explode(",", $rights); $this->endDate = DateTime::createFromFormat("Y-m-d", $endDate); // @todo It would be safer to set an endDate in the past as "default" value if ($this->endDate == false) { $this->endDate = null; } } /** Getter for the userId * * @return int >0 representing the id of the user the apiKey is for */ public function getUserId() { return $this->userId; } /** Getter for the apiKey * * @return string base62 coded string representing the apiKey */ public function getKey() { return $this->key; } /** Checking if the apiKey is valid for a certain action * * @param string $action the action to test the apiKey against * @return boolean true if apiKey is valid for the action, false otherwise */ public function isValidFor(string $action) { // @todo add as validation: does the user exist and is 'active' (?) $today = new DateTime(); return $this->id != null && in_array($action, $this->rights) && $this->endDate->format("Y-m-d") >= $today->format("Y-m-d"); } /** request a specific apiKey from the db * * @param string $key the key to request * @return ApiKey found in the db, null otherwise */ public static function loadFromDb(string $key) { if (!self::isWellFormatted($key)) { return null; } $query = "SELECT * FROM `cwsvjudo_main`.`participo_apiKeys` WHERE apiKey = :key;"; $params = [":key" => ["value" => $key, "data_type" => PDO::PARAM_STR]]; $response = dbConnector::query($query, $params); // apiKeys are considered unique. so every other count is treated as error to prevent unprivileged access if (count($response) != 1) { return null; } return ApiKey::fromDbArray($response[0]); } /** Add a key to the DB * * @return void */ public function addToDb() { $query = "INSERT INTO `cwsvjudo_main`.`participo_apiKeys` (userId, apiKey, rights, endDate) VALUES (:userId, :apiKey, :rights, :endDate);"; $params = [ ":userId" => [ "value" => $this->userId, "data_type" => PDO::PARAM_INT, ], ":apiKey" => ["value" => $this->key, "data_type" => PDO::PARAM_STR], ":rights" => [ "value" => implode(",", $this->rights), "data_type" => PDO::PARAM_STR, ], ":endDate" => [ "value" => $this->endDate->format("Y-m-d"), "data_type" => PDO::PARAM_STR, ], ]; $response = dbConnector::query($query, $params); // @todo use the response in an error handling/messaging // @todo differentiate between inserting and updating if the id is set it should only be updated (e.g. prolonging) } /** create an Api key from the return of an sql select */ private static function fromDbArray(array $apiKey) { return new ApiKey( $apiKey["id"] ?? null, $apiKey["userId"] ?? null, $apiKey["apiKey"] ?? null, $apiKey["rights"] ?? null, $apiKey["endDate"] ?? null, ); } /** * List of symbols that can be used for the encoding * * @var string */ private static $BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** quick and dirty implementation of a convert_to_base62 * * inspired by https://stackoverflow.com/a/4964352 * * @param int $num * @param integer $base * @return void */ private static function toBase(int $num, int $base = 62): string { // @todo Silent error: Replacing invalid input will lead to unexpected behaviour. $base = filter_var($base, FILTER_VALIDATE_INT, [ "options" => ["default" => strlen(self::$BASE), "min_range" => 1], ]); // @todo What is with negative numbers? How are they supposed to be converted? $r = $num % $base; $res = ApiKey::$BASE[$r]; $q = floor($num / $base); while ($q) { $r = $q % $base; $q = floor($q / $base); $res = ApiKey::$BASE[$r] . $res; } return $res; } /** simple check if a string is a well formatted apiKey * * Basically checks, if it consists only of 0-9, a-z or A-Z * * @param string $string string to check * @return boolean true if it is base62 encoded, false otherwise */ public static function isWellFormatted(string $string) { return (bool) preg_match('/^[0-9a-zA-Z]+$/', $string); } /** provides a random api key value * * @return string a random api key value */ public static function create() { // @todo What is with negative numbers? How are they supposed to be converted? return ApiKey::toBase(random_int(0, PHP_INT_MAX)); } /** more of a backup */ private static function createTable() { dbConnector::query( "CREATE TABLE `cwsvjudo_main`.`participo_apiKeys` (`id` INT NOT NULL AUTO_INCREMENT COMMENT 'unique identifier' , `userId` INT NOT NULL COMMENT 'id of the user the key belongs to' , `apiKey` VARCHAR(16) NOT NULL COMMENT 'the apiKey itself' , `rights` INT NOT NULL COMMENT 'a comma separated list of rights for the key' , `endDate` DATE NOT NULL COMMENT 'endDate for the apiKey' , PRIMARY KEY (`id`), UNIQUE (`key`)); ", ); } }