Image now a file

This commit is contained in:
Kilian Hofmann 2024-07-21 18:28:19 +02:00
parent 8d91e805dd
commit b3c5841e36
17 changed files with 346 additions and 36 deletions

View File

@ -14,6 +14,11 @@ RewriteRule ^phpCourse/exam/vendor/.* index.php [L,NC]
RewriteRule ^phpCourse/exam/routes/.* index.php [L,NC]
RewriteRule ^phpCourse/exam/react/.* index.php [L,NC]
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} !/uploads/.*
RewriteCond %{REQUEST_FILENAME} !/dist/.*
RewriteRule ^ dist [L,NC]
##
## API routes
##

View File

@ -9,8 +9,13 @@ class Login
{
public function post()
{
$email = Input::post("email");
if (empty($email)) throw new Exception("Missing email", 400);
$password = Input::post("password");
if (empty($password)) throw new Exception("Missing Password", 400);
try {
$response = \Khofmann\Models\User\User::logIn(Input::post("email"), Input::post("password"));
$response = \Khofmann\Models\User\User::logIn($email, $password);
return json_encode($response);
} catch (Exception $err) {
switch ($err->getMessage()) {
@ -20,6 +25,8 @@ class Login
throw new Exception("User not Found", 404);
case "Invalid":
throw new Exception("Invalid Username or Password", 401);
default:
throw $err;
}
}
}

View File

@ -9,6 +9,6 @@ class Logout
public function post()
{
$token = request()->getHeader("token");
return json_decode(User::getByToken($token)->logOut($token));
return json_decode(User::getByToken($token)->logOut());
}
}

48
exam/api/User/User.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace Api\User;
use Exception;
use Khofmann\Models\User\User as MUser;
use Khofmann\Input\Input;
class User
{
public function get($id)
{
try {
return json_encode(MUser::getByID($id));
} catch (Exception $err) {
switch ($err->getMessage()) {
case "NotFound":
throw new Exception("User not Found", 404);
default:
throw $err;
}
}
}
public function post($id)
{
$username = Input::post("username");
$password = Input::post("password");
$image = Input::file("image");
try {
return json_encode(MUser::getByID($id)->update($username, $password, $image));
} catch (Exception $err) {
switch ($err->getMessage()) {
case "NotFound":
throw new Exception("User not Found", 404);
case "FailedUsername":
throw new Exception("Failed to update username", 500);
case "FailedPassword":
throw new Exception("Failed to update password", 500);
case "FailedImage":
throw new Exception("Failed to update image", 500);
default:
throw $err;
}
}
}
}

View File

@ -27,6 +27,15 @@ paths:
examples:
Success:
value: true
400:
description: Missing Fields
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
Missing Fields:
value: { "message": "Missing email" }
401:
description: Invalid credentials
content:
@ -74,6 +83,97 @@ paths:
value: true
tags:
- Login/Logout
/user{id}:
get:
summary: Get user
description: Get user by ID
security:
- BasicAuth: []
parameters:
- name: id
in: path
description: User ID
required: true
schema:
type: integer
format: int14
responses:
200:
description: Success
content:
application/json:
schema:
$ref: "#/components/schemas/UserResponse"
examples:
Success:
value:
{
"id": 1,
"username": "Admin",
"status": 1,
"email": "marvin@zedat.fu-berlin.de",
"image": "profilbilder\\/admin.svg",
"isAdmin": true,
}
404:
description: User not Found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
User not Found:
value: { "message": "User not Found" }
tags:
- User
post:
summary: Update user
description: Update user with ID. Fields are updated in order username,password,image. If one fails, subsequent are not updated
security:
- BasicAuth: []
parameters:
- name: id
in: path
description: User ID
required: true
schema:
type: integer
format: int14
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UserUpdateRequest"
responses:
200:
description: Success
content:
application/json:
schema:
$ref: "#/components/schemas/BooleanResponse"
examples:
Success:
value: true
404:
description: User not Found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
User not Found:
value: { "message": "User not Found" }
500:
description: Update failed
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
User not Found:
value: { "message": "Failed to update username" }
tags:
- User
externalDocs:
url: https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/docs/
@ -103,6 +203,31 @@ components:
type: string
password:
type: string
UserResponse:
type: object
properties:
id:
type: number
username:
type: string
status:
type: number
email:
type: string
image:
type: string
nullable: true
isAdmin:
type: boolean
UserUpdateRequest:
type: object
properties:
username:
type: string
password:
type: string
image:
type: string
securitySchemes:
BasicAuth:
type: apiKey
@ -110,5 +235,4 @@ components:
in: header
tags:
- name: Login/Logout
- name: Users
- name: Posts
- name: User

File diff suppressed because one or more lines are too long

30
exam/classes/.htaccess Normal file
View File

@ -0,0 +1,30 @@
RewriteEngine On
##
## You may need to uncomment the following line for some hosting environments,
## if you have installed to a subdirectory, enter the name here also.
##
RewriteBase /phpCourse/exam
##
## Black listed folders
##
RewriteRule ^phpCourse/exam/config/.* index.php [L,NC]
RewriteRule ^phpCourse/exam/vendor/.* index.php [L,NC]
RewriteRule ^phpCourse/exam/routes/.* index.php [L,NC]
RewriteRule ^phpCourse/exam/react/.* index.php [L,NC]
##
## API routes
##
RewriteCond %{REQUEST_FILENAME} /api/.*
RewriteCond %{REQUEST_FILENAME} !/api/docs
RewriteRule ^ api/index.php [L,NC,QSA]
##
## Standard routes
##
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !/api/docs
RewriteCond %{REQUEST_FILENAME} !/dist
RewriteRule ^ dist [L,NC,QSA]

View File

@ -0,0 +1,31 @@
<?php
namespace Khofmann\Auth;
use Exception;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Khofmann\Models\User\User;
class AdminAuth implements IMiddleware
{
public function handle(Request $request): void
{
$token = $request->getHeader("token");
// No token
if ($token === null) {
response()->httpCode(401)->json(["message" => "Not Authorized"]);
}
try {
$user = User::getByToken($token);
if (!$user->getIsAdmin()) {
response()->httpCode(401)->json(["message" => "Not Authorized"]);
}
} catch (Exception $err) {
// No user with this token exists
response()->httpCode(401)->json(["message" => "Not Authorized"]);
}
}
}

View File

@ -4,17 +4,18 @@ namespace Khofmann\Input;
class Input
{
private static function input($index = null, $defaultValue = null, ...$methods)
{
if ($index !== null) {
return request()->getInputHandler()->value($index, $defaultValue, ...$methods);
}
return request()->getInputHandler();
}
public static function post($index, $defaultValue = null)
{
return input()->post($index, $defaultValue);
return request()->getInputHandler()->post($index, $defaultValue);
}
public static function get($index, $defaultValue = null)
{
return request()->getInputHandler()->get($index, $defaultValue);
}
public static function file($index, $defaultValue = null)
{
return request()->getInputHandler()->file($index, $defaultValue);
}
}

View File

@ -5,6 +5,7 @@ namespace Khofmann\Models\User;
use Exception;
use PDO;
use Khofmann\Database\Database;
use Config\Config;
use JsonSerializable;
class User implements JsonSerializable
@ -40,8 +41,7 @@ class User implements JsonSerializable
$stmt->execute();
$data = $stmt->fetch();
if (!$data)
throw new Exception("No user found");
if (!$data) throw new Exception("NotFound");
return new User($id, $data["benutzer"], $data["status"], $data["email"], $data["image"], $data["isadmin"] === 1);
}
@ -56,8 +56,7 @@ class User implements JsonSerializable
$stmt->execute();
$data = $stmt->fetch();
if (!$data)
throw new Exception("No user found");
if (!$data) throw new Exception("NotFound");
return new User($data["id"], $data["benutzer"], $data["status"], $email, $data["image"], $data["isadmin"] === 1);
}
@ -72,8 +71,7 @@ class User implements JsonSerializable
$stmt->execute();
$data = $stmt->fetch();
if (!$data)
throw new Exception("No user found");
if (!$data) throw new Exception("NotFound");
return new User($data["id"], $data["benutzer"], $data["status"], $data["email"], $data["image"], $data["isadmin"] === 1);
}
@ -127,15 +125,49 @@ class User implements JsonSerializable
* Members
*/
public function logOut(string $token): bool
public function logOut(): bool
{
$db = Database::getInstance();
// Get user data
$stmt = $db->prepare("UPDATE egb_benutzer SET token = NULL WHERE id = :ID");
$stmt->bindValue(":ID", $this->id);
return $stmt->execute();
}
public function update(?string $username, ?string $password, $image = null)
{
$db = Database::getInstance();
$error = false;
if (!empty($username)) {
$stmt = $db->prepare("UPDATE egb_benutzer SET benutzer = :USR WHERE id = :ID");
$stmt->bindValue(":USR", $username);
$stmt->bindValue(":ID", $this->id);
$error = !$stmt->execute();
}
if ($error) throw new Exception("FailedUsername");
if (!empty($password)) {
$stmt = $db->prepare("UPDATE egb_benutzer SET passwort = :PAS WHERE id = :ID");
$stmt->bindValue(":PAS", password_hash($password, PASSWORD_DEFAULT));
$stmt->bindValue(":ID", $this->id);
$error = !$stmt->execute();
}
if ($error) throw new Exception("FailedPassword");
if (!empty($image)) {
$destinationFilename = sprintf('%s.%s', uniqid(), $image->getExtension());
$image->move(Config::getBaseFSPath() . "uploads/profilbilder/$destinationFilename");
$stmt = $db->prepare("UPDATE egb_benutzer SET image = :IMG WHERE id = :ID");
$stmt->bindValue(":IMG", $destinationFilename);
$stmt->bindValue(":ID", $this->id);
$error = !$stmt->execute();
}
if ($error) throw new Exception("FailedImage");
return true;
}
/*
* Getters
*/
@ -160,7 +192,7 @@ class User implements JsonSerializable
return $this->email;
}
public function getImage(): string
public function getImage(): ?string
{
return $this->image;
}

View File

@ -39,6 +39,11 @@ class Config
return Config::getInstance()->app["basePath"];
}
public static function getBaseFSPath()
{
return Config::getInstance()->app["baseFSPath"];
}
public static function getDatabase()
{
return Config::getInstance()->database;

View File

@ -2,4 +2,5 @@
return [
"basePath" => "phpCourse/exam/",
"baseFSPath" => "/home/k/khofmann/public_html/phpCourse/exam/"
];

5
exam/dist/index.html vendored Normal file
View File

@ -0,0 +1,5 @@
<h1>Redirect to React</h1>
<form action="/phpCourse/exam/api/user/1" enctype="multipart/form-data" method="POST">
<input type="file" name="image" />
<input type="submit">Send</input>
</form>

1
exam/dist/index.php vendored
View File

@ -1 +0,0 @@
<h1>Redirect to React</h1>

View File

@ -3,7 +3,7 @@
use Pecee\SimpleRouter\SimpleRouter;
use Pecee\Http\Request;
// Error handling
SimpleRouter::error(function (Request $request, \Exception $exception) {
SimpleRouter::error(function (Request $request, Exception $exception) {
$code = $exception->getCode();
response()->httpCode(is_int($code) ? $code : 500)->json(["message" => $exception->getMessage()]);
});
@ -17,8 +17,7 @@ SimpleRouter::group(["middleware" => \Khofmann\Auth\Auth::class], function () {
SimpleRouter::post("/logout", [Api\Logout\Logout::class, "post"]);
});
// User
SimpleRouter::group(["middleware" => \Khofmann\Auth\Auth::class], function () {
SimpleRouter::get("/user/{id}", function ($userID) {
echo "USER ENDP $userID";
});
});
//SimpleRouter::group(["middleware" => \Khofmann\Auth\Auth::class], function () {
SimpleRouter::get("/user/{id}", [Api\User\User::class, "get"]);
SimpleRouter::post("/user/{id}", [Api\User\User::class, "post"]);
//});

2
exam/uploads/profilbilder/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -69,4 +69,4 @@ function csrf_token(): ?string
}
return null;
}
}