Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ indent_style = space
insert_final_newline = true
max_line_length = 120
tab_width = 4

[openapi.yaml]
indent_size = 2
56 changes: 56 additions & 0 deletions src/controller/ResourceController.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
<?php namespace XFRM\Controller;
defined('_XFRM_API') or exit('No direct script access allowed here.');

use XFRM\Object\Error;
use XFRM\Object\Resource as Resource;
use XFRM\Object\ResourcePurchase;
use XFRM\Support\Database;
use XFRM\Util\ApiKeyUtil;
use XFRM\Util\RequestUtil as Req;

class ResourceController
{
/**
* @var Database
*/
private $database;

public function __construct($database)
Expand Down Expand Up @@ -61,4 +68,53 @@ public function getResourcesByAuthor()

return $out;
}

public function getResourcePurchases()
{
$resource = $this->_getOwnedPremiumResource();
$purchases = $this->database->getResourcePurchases($resource->resource_id, Req::page());
if (!is_null($purchases)) {
$out = [];
foreach ($purchases as $purchase) {
$out[] = new ResourcePurchase($purchase);
}
return $out;
}
return NULL;
}

public function getResourcePurchaseByUser()
{
if (!Req::checkUserIdParam()) {
return NULL;
}
$resource = $this->_getOwnedPremiumResource();
$purchase = $this->database->getResourcePurchaseByUser($resource->resource_id, Req::userId());
if (!is_null($purchase)) {
return new ResourcePurchase($purchase);
}
return NULL;
}

private function _getOwnedPremiumResource()
{
if (Req::checkIdParam() && Req::checkApiKeyParam()) {
$apiKeyOwnerId = ApiKeyUtil::validateApiKey($this->database, Req::apiKey());
$resource = $this->database->getResource(Req::id());
if (is_null($resource) || $resource === false) {
return NULL;
}
if (empty($resource->currency)) {
echo new Error(400, "Not a premium resource.");
exit();
}
if ($resource->user_id !== $apiKeyOwnerId) {
echo new Error(403, "No access to requested resource.");
exit();
}
return $resource;
}
return NULL;
}

}
1 change: 1 addition & 0 deletions src/imports.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

require_once(__DIR__ . '/util/IconUtil.php');
require_once(__DIR__ . '/util/RequestUtil.php');
require_once(__DIR__ . '/util/ApiKeyUtil.php');

require_once(__DIR__ . '/controller/AuthorController.php');
require_once(__DIR__ . '/controller/ResourceController.php');
Expand Down
19 changes: 19 additions & 0 deletions src/object/ResourcePurchase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php namespace XFRM\Object;
defined('_XFRM_API') or exit('No direct script access allowed here.');

class ResourcePurchase
{

public $resource_id;

public $user_id;

public function __construct($database_entry)
{
// TODO: database structure
$this->resource_id = $database_entry->resource_id;
$this->user_id = $database_entry->user_id;
}


}
83 changes: 83 additions & 0 deletions src/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,70 @@ paths:
type: array
items:
$ref: '#/components/schemas/ResourceUpdate'
/index.php?action=getResourcePurchases:
parameters:
- name: id
description: The resource ID
in: query
required: true
schema:
type: integer
- name: page
description: The page of results to get
in: query
required: false
schema:
type: integer
get:
operationId: getResourcePurchases
summary: Obtain all purchases of a (premium) resource
security:
- apiKeyAuthorization: [ ]
responses:
'200':
description: An object containing an array with all purchases / purchasers of the resources
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ResourcePurchase'
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
/index.php?action=getResourcePurchaseByUser:
parameters:
- name: id
description: The resource ID
in: query
required: true
schema:
type: integer
- name: user_id
description: The id of the purchaser
in: query
required: true
schema:
type: integer
get:
operationId: getResourcePurchaseByUser
summary: Obtain the details of a purchase made by a specific user for a (premium) resource
security:
- apiKeyAuthorization: [ ]
responses:
'200':
description: An object containing the details of the purchases of the resources
content:
application/json:
schema:
$ref: '#/components/schemas/ResourcePurchase'
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
'404':
description: The specified user did not buy the resource.
/index.php?action=getAuthor:
parameters:
- name: id
Expand Down Expand Up @@ -169,6 +233,16 @@ paths:
schema:
$ref: '#/components/schemas/Author'
components:
securitySchemes:
apiKeyAuthorization:
type: apiKey
in: header
name: X-API-Key
responses:
UnauthorizedError:
description: Invalid API Key
ForbiddenError:
description: No permission to access the resource (API Key does not permit access to given resource).
schemas:
Resource:
type: object
Expand Down Expand Up @@ -272,6 +346,15 @@ components:
type: string
description:
type: string
ResourcePurchase:
type: object
properties:
resource_id:
type: integer
description: The ID of the purchases resource
user_id:
type: integer
description: The ID of the user that purchased the resource
Author:
type: object
properties:
Expand Down
62 changes: 61 additions & 1 deletion src/support/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function __construct($username, $password, $hostname, $port, $database)
}
}

public static function initializeViaConfig()
public static function initializeViaConfig(): Database
{
return new Database(
Config::$data['MYSQL_USERNAME'],
Expand Down Expand Up @@ -157,6 +157,46 @@ public function getResourceUpdates($resource_id, $page)
return NULL;
}

public function getResourcePurchases($resource_id, $page)
{
$page = $page == 1 ? 0 : 10 * ($page - 1);
if (is_null($this->conn)) {
return NULL;
}
// TODO: I have no clue how the tables look like
$statement = $this->conn->prepare(
"SELECT whatever
FROM xf_resource_XXXXX
WHERE resource_id = :resource_id
LIMIT 10 OFFSET :offset"
);
$statement->bindParam(':resource_id', $resource_id);
$statement->bindParam(':offset', $page, \PDO::PARAM_INT);
if ($statement->execute()) {
return $statement->fetchAll();
}
return NULL;
}

public function getResourcePurchaseByUser($resource_id, $user_id)
{
if (is_null($this->conn)) {
return NULL;
}
// TODO: I (still) have no clue how the tables look like
$statement = $this->conn->prepare(
"SELECT whatever, something_else
FROM xf_resource_XXXXX
WHERE resource_id = :resource_id AND purchaser_id = :user_id"
);
$statement->bindParam(':resource_id', $resource_id);
$statement->bindParam(':user_id', $user_id);
if ($statement->execute()) {
return $statement->fetch();
}
return NULL;
}

public function getUser($user_id)
{
if (!is_null($this->conn)) {
Expand Down Expand Up @@ -214,6 +254,26 @@ public function findUser($username)
return NULL;
}

/**
* @param string $apiKey
* @return integer|null
*/
public function getApiKeyOwnerId(string $apiKey)
{
if (is_null($this->conn)) {
return NULL;
}
$statement = $this->conn->prepare("SELECT user_id FROM xf_api_keys WHERE key = :key LIMIT 1");
$statement->bindParam(':key', $apiKey);
if ($statement->execute()) {
$fetched = $statement->fetch();
if (!is_null($fetched) && $fetched !== false) {
return $fetched['user_id'];
}
}
return NULL;
}

private function _resource($additional_where_clauses, $limit = 1, $offset = null)
{
$offsetClause = is_null($offset) ? '' : 'OFFSET :offset';
Expand Down
12 changes: 12 additions & 0 deletions src/support/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public function __construct()
"listResourceCategories",
"getResourceUpdate",
"getResourceUpdates",
"getResourcePurchases",
"getResourcePurchaseByUser",
"getAuthor",
"findAuthor"
];
Expand Down Expand Up @@ -80,6 +82,16 @@ private function getResourceUpdates()
return $this->resourceUpdateController->getResourceUpdates();
}

private function getResourcePurchases()
{
return $this->resourceController->getResourcePurchases();
}

private function getResourcePurchaseByUser()
{
return $this->resourceController->getResourcePurchaseByUser();
}

private function getAuthor()
{
return $this->authorController->getAuthor();
Expand Down
25 changes: 25 additions & 0 deletions src/util/ApiKeyUtil.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php namespace XFRM\Util;
defined('_XFRM_API') or exit('No direct script access allowed here.');

use XFRM\Object\Error;
use XFRM\Support\Database;

class ApiKeyUtil
{

/**
* @param $database Database
* @param $apiKey string
* @return integer the user id of the API keys owner
*/
public static function validateApiKey(Database $database, string $apiKey): int
{
$userId = $database->getApiKeyOwnerId($apiKey);
if (!is_null($userId)) {
return $userId;
}
echo new Error(401, "Invalid API Key.");
exit();
}

}
Loading