Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ Options +FollowSymlinks

RewriteEngine On

# ! < TEAMS API > ! #
RewriteRule ^api/v1/(teams\/.*|teams)$ /api/internal/v1/teams/router.php [L,NC]
# ! </ TEAMS API > ! #

RewriteBase /


Expand Down
55 changes: 52 additions & 3 deletions global/php/api_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,20 @@ public function delete(): ApiResult {
}
}

class ParameterizedApiController extends ApiController {
private array $parameters;

public function __construct(array $parameters) {
$this->parameters = $parameters;
}

protected function getParameter(mixed $key): mixed {
return $this->parameters[$key];
}
}

class JsonBodyReader {
public static function read() {
public static function read(): array {
$result = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE)
throw new Exception("could not decode json, code: " . json_last_error());
Expand All @@ -127,7 +139,7 @@ public function getContentType(): string {
}

class ApiControllerExecutor {
public static function execute(ApiController $controller, ApiResultSerializer $serializer) {
public static function execute(mixed $controller, ApiResultSerializer $serializer) {
try {
$method = $_SERVER['REQUEST_METHOD'];

Expand Down Expand Up @@ -157,11 +169,48 @@ public static function execute(ApiController $controller, ApiResultSerializer $s
} catch (Exception $exception) {
error_log($exception->getMessage());
Logging::PutLog("Got exception in " . $controller::class . ":" . $exception->getMessage());
$result = new UnknownErrorResult();
$result = new UnknownErrorResult(["message" => $exception->getMessage()]);
}

http_response_code($result->getStatusCode());
header("Content-Type: " . $serializer->getContentType());
echo $serializer->serialize($result);
}
}

class ApiRouter {
private static ?ApiResultSerializer $defaultSerializer;

public static function getSerializer(): ApiResultSerializer {
if (!isset(self::$defaultSerializer))
self::$defaultSerializer = new JsonApiResultSerializer;

return self::$defaultSerializer;
}

public static function setSerializer(ApiResultSerializer $serializer): void {
self::$defaultSerializer = $serializer;
}

public static function on(string $regex, string $controller_class_name): void {
$params = $_SERVER['REQUEST_URI'];
$params = (stripos($params, "/") !== 0) ? "/" . $params : $params;
$regex = str_replace('/', '\/', $regex);

$is_match = preg_match('/^' . ($regex) . '$/', $params, $matches, PREG_OFFSET_CAPTURE);

if ($is_match) {
// first value is normally the route, lets remove it
array_shift($matches);
// Get the matches as parameters
$params = array_map(function ($param) {
return $param[0];
}, $matches);

$ref = new ReflectionClass($controller_class_name);
$instance = $ref->newInstanceArgs(array($params));

ApiControllerExecutor::execute($instance, self::getSerializer());
}
}
}
5 changes: 5 additions & 0 deletions global/php/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -759,4 +759,9 @@ function osekai_http_request()
function json_validator()
{
require_once($_SERVER['DOCUMENT_ROOT'] . "/global/php/json_validator.php");
}

function teams_rules()
{
require_once($_SERVER['DOCUMENT_ROOT'] . "/global/php/rules/teams.php");
}
81 changes: 69 additions & 12 deletions global/php/json_validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,90 @@ public function __construct() {
$this->rules = array();
}

private function add_function_rule(\Closure $closure) {
array_push($this->rules, $closure);
private function add_function_rule(string $rule_code, \Closure $closure, ?array $details = null) {
array_push($this->rules, [$closure, $rule_code, $details]);
}

public function must_be_int(): JsonValidatorRule {
$this->add_function_rule(function($v) { return is_int($v); });
$this->add_function_rule("MUST_BE_INT", function($v) { return is_int($v); });

return $this;
}

public function validate($v): bool {
public function must_be_string(): JsonValidatorRule {
$this->add_function_rule("MUST_BE_STRING", function($v) { return is_string($v); });

return $this;
}

public function string_must_have_length($minLength, $maxLength): JsonValidatorRule {
$this->add_function_rule(
"STRING_MUST_HAVE_LENGTH",
function($v) use ($minLength, $maxLength) {
$len = strlen($v);
return $minLength <= $len && $len <= $maxLength;
},
[ "min_length" => $minLength, "max_length" => $maxLength ]
);

return $this;
}

public function must_be_defined(): JsonValidatorRule {
$this->add_function_rule("MUST_BE_DEFINED", function($v) {
return isset($v);
});

return $this;
}

public function validate($v): JsonValidationResult {
foreach ($this->rules as $rule) {
if (!$rule($v))
return false;
if (!$rule[0]($v))
return new JsonValidationResult(false, $rule[1], $rule[2]);
}

return true;
return new JsonValidationResult(true);;
}
}

class JsonValidationResult {
private bool $success;
private ?string $invalid_rule_code;
private ?array $invalid_rule_code_details;

public function __construct(
bool $success,
?string $invalid_rule_code = null,
?array $invalid_rule_code_details = null)
{
$this->success = $success;
$this->invalid_rule_code = $invalid_rule_code;
$this->invalid_rule_code_details = $invalid_rule_code_details;
}

public function isSuccess(): bool {
return $this->success;
}

public function getInvalidRuleCode(): ?string {
return $this->invalid_rule_code;
}

public function getInvalidRuleDetails(): ?array {
return $this->invalid_rule_code_details;
}
}

class JsonValidator {
public static function validate_associative_array(array $array, array $rules): bool {
foreach ($array as $key => $value) {
if (isset($rules[$key]) && !$rules[$key]->validate($value))
return false;
public static function validate_associative_array(array $array, array $rules): JsonValidationResult {
foreach ($rules as $key => $rule) {
$validationResult = $rule->validate($array[$key]);

if (!$validationResult->isSuccess())
return $validationResult;
}

return true;
return new JsonValidationResult(true);
}
}
12 changes: 12 additions & 0 deletions global/php/rules/teams.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

define("TEAM_NAME_MAX_LENGTH", 32);
define("TEAM_NAME_MIN_LENGTH", 4);
define("TEAM_BIO_MAX_LENGTH", 250);
define("TEAM_BIO_MIN_LENGTH", 1);
define("TEAM_USERPAGE_MAX_LENGTH", 5000);
define("TEAM_USERPAGE_MIN_LENGTH", 1);
define("TEAM_AVATAR_MAX_WIDTH", 512);
define("TEAM_AVATAR_MAX_HEIGHT", 512);
define("TEAM_BANNER_MAX_WIDTH", 1100);
define("TEAM_BANNER_MAX_HEIGHT", 300);
2 changes: 1 addition & 1 deletion medals/api/favourite.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function put(): ApiResult {

if (!JsonValidator::validate_associative_array($requestJson, [
'medal_id' => (new JsonValidatorRule())->must_be_int()
])) {
])->isSuccess()) {
return new BadArgumentsApiResult(["message" => "Invalid medal_id"]);
}

Expand Down