Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #104 from Dino-Kupinic/develop
Browse files Browse the repository at this point in the history
upload and parsing of xlsx file
  • Loading branch information
Dino-Kupinic authored Apr 25, 2024
2 parents f904d28 + 3888d5e commit 6b72d85
Show file tree
Hide file tree
Showing 25 changed files with 9,826 additions and 6,709 deletions.
2 changes: 2 additions & 0 deletions backend/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"doctrine/doctrine-bundle": "^2.11",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^3.1",
"nelmio/cors-bundle": "^2.4",
"lexik/jwt-authentication-bundle": "^2.20",
"nixilla/php-jwt": "^0.1.1",
"phpdocumentor/reflection-docblock": "^5.3",
"phpoffice/phpspreadsheet": "^2.0",
"phpstan/phpdoc-parser": "^1.26",
"symfony/asset": "7.0.*",
"symfony/asset-mapper": "7.0.*",
Expand Down
2,264 changes: 1,596 additions & 668 deletions backend/composer.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions backend/config/bundles.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
];
6 changes: 6 additions & 0 deletions backend/config/packages/lexik_jwt_authentication.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%' # required for token creation
public_key: '%env(resolve:JWT_PUBLIC_KEY)%' # required for token verification
pass_phrase: '%env(JWT_PASSPHRASE)%' # required for token creation
token_ttl: 3600 # in seconds, default is 3600
10 changes: 10 additions & 0 deletions backend/config/packages/nelmio_cors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
nelmio_cors:
defaults:
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Content-Type', 'Authorization']
expose_headers: ['Link']
max_age: 3600
paths:
'^/': null
3 changes: 0 additions & 3 deletions backend/importmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,4 @@
'@hotwired/turbo' => [
'version' => '7.3.0',
],
'eslint-config-prettier' => [
'version' => '9.1.0',
],
];
38 changes: 38 additions & 0 deletions backend/src/Controller/ImportController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Controller;

use App\Service\ImportService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route("api/v1")]
class ImportController extends AbstractController
{
#[Route("/importXLSX", name: "app_import", methods: "POST")]
public function index(ImportService $importService, Request $request): Response
{
$uploadedFile = $request->files->get("file");

if (!$uploadedFile) {
return new Response("No file provided", Response::HTTP_BAD_REQUEST);
}

$filePath = $uploadedFile->getPathname();
$data = $importService->parseFile($filePath);

$header = $data[0];
if ($importService->isHeaderValid($header)) {
unset($data[0]);
$importService->persist($data);
return new Response("Success", Response::HTTP_OK);
}

return new Response(
"XLSX Header is invalid, did you provide the correct file?",
Response::HTTP_BAD_REQUEST
);
}
}
25 changes: 12 additions & 13 deletions backend/src/Controller/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route("api/v1")]
class LoginController extends AbstractController
{
#[Route('/ldaps', name: 'app_ldap')]
public function index(AuthService $authService, Request $request, Kernel $kernel): Response
{
$user = $request->get("usr");
$pwd = $request->get("pwd");
$status =$authService->authenticateUser($user, $pwd);
#[Route('/ldaps', name: 'app_ldap')]
public function index(AuthService $authService, Request $request, Kernel $kernel): Response
{
$user = $request->get("usr");
$pwd = $request->get("pwd");
$status = $authService->authenticateUser($user, $pwd);

$key = file_get_contents($kernel->getProjectDir() . "/config/jwt/private.pem");
$token = new AuthToken($key);
$token->setValue(['user'=>$user, 'authorized'=>$status]);
$key = file_get_contents($kernel->getProjectDir() . "/config/jwt/private.pem");
$token = new AuthToken($key);
$token->setValue(['user' => $user, 'authorized' => $status]);



return new JsonResponse($token, 200, [], true);
}
return new JsonResponse($token, 200, [], true);
}
}
11 changes: 5 additions & 6 deletions backend/src/Controller/TestingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@

class TestingController extends AbstractController
{
#[Route('/test/example', name: 'app_testing')]
public function index(): Response
{
return $this->json(['name' => 'John']);
}

#[Route('/test/example', name: 'app_testing')]
public function index(): Response
{
return $this->json(['name' => 'John']);
}
}
93 changes: 45 additions & 48 deletions backend/src/Entity/AuthToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,61 @@

class AuthToken
{
private String $key;
public String $jwtString;
public bool $success;

/**
* @param $key
*/
public function __construct($key)
{
$this->key = $key;
}
private string $key;
public string $jwtString;
public bool $success;

/**
* @param $key
*/
public function __construct($key)
{
$this->key = $key;
}

public function getValue(): string
{
return $this->jwtString;
}

public function setValue(array $payload): void
{
$this->jwtString = $this->encode($payload);
}
public function getValue(): string
{
return $this->jwtString;
}

public function isSuccess(): bool
{
return $this->success;
}
public function setValue(array $payload): void
{
$this->jwtString = $this->encode($payload);
}

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

public function setSuccess(bool $success): void
{
$this->success = $success;
}

private function encode(array $data): string
{
try {
$data = array_merge($data, ['iat' => (int)date('U')]);
return JWT::encode($data, $this->key);
}
catch (Exception $e) {
throw new JWTEncodeFailureException(JWTEncodeFailureException::INVALID_CONFIG, 'An error occurred while trying to encode the JWT token.', $e);
}
}

public function decode($token): array
{
try {
return (array) JWT::decode($token, $this->key);
} catch (Exception $e) {
throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'Invalid JWT Token', $e);
}
private function encode(array $data): string
{
try {
$data = array_merge($data, ['iat' => (int)date('U')]);
return JWT::encode($data, $this->key);
} catch (Exception $e) {
throw new JWTEncodeFailureException(JWTEncodeFailureException::INVALID_CONFIG, 'An error occurred while trying to encode the JWT token.', $e);
}
}

public function __toString(): string
{
return $this->jwtString;
public function decode($token): array
{
try {
return (array)JWT::decode($token, $this->key);
} catch (Exception $e) {
throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'Invalid JWT Token', $e);
}
}


public function __toString(): string
{
return $this->jwtString;
}
}
53 changes: 27 additions & 26 deletions backend/src/Service/AuthService.php
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
<?php

namespace App\Service;

use PHPUnit\Util\Exception;

class AuthService
{
public function authenticateUser($user, $password): bool
{
$success = null;
try {
$ds = ldap_connect($_ENV["LDAP_URL"]) or throw new Exception("Could not connect to LDAP server.");
if ($ds) {
/**
* LDAP-Request for school
*
* $r = ldap_bind($ds) or throw new Exception("Error trying to bind: " . ldap_error($ds));
* $sr = ldap_search($ds, $_ENV["SCHULE_BASE"], "cn=$user", array("dn")) or throw new LdapException ("Error in search query: " . ldap_error($ds));
* $res = ldap_get_entries($ds, $sr);
* $userDN = $res[0]['dn'];
*/
public function authenticateUser($user, $password): bool
{
$success = null;
try {
$ds = ldap_connect($_ENV["LDAP_URL"]) or throw new Exception("Could not connect to LDAP server.");
if ($ds) {
/**
* LDAP-Request for school
*
* $r = ldap_bind($ds) or throw new Exception("Error trying to bind: " . ldap_error($ds));
* $sr = ldap_search($ds, $_ENV["SCHULE_BASE"], "cn=$user", array("dn")) or throw new LdapException ("Error in search query: " . ldap_error($ds));
* $res = ldap_get_entries($ds, $sr);
* $userDN = $res[0]['dn'];
*/

/**
* LDAP-Request for Test-Environment
*/
$userDN = "cn=$user,ou=TestUsers," . $_ENV["LDAP_BASE"];
/**
* LDAP-Request for Test-Environment
*/
$userDN = "cn=$user,ou=TestUsers," . $_ENV["LDAP_BASE"];

$success = @ldap_bind($ds, $userDN, $password) or throw new Exception("Error trying to bind: " . ldap_error($ds));
$success = @ldap_bind($ds, $userDN, $password) or throw new Exception("Error trying to bind: " . ldap_error($ds));

ldap_close($ds);
}
} catch (Exception $e) {
$success = false;
}
return $success;
ldap_close($ds);
}
} catch (Exception $e) {
$success = false;
}
return $success;
}
}

?>
?>
47 changes: 47 additions & 0 deletions backend/src/Service/ImportService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace App\Service;

use PhpOffice\PhpSpreadsheet\IOFactory;

class ImportService
{
/**
* @param string $filePath
* @return array array containing the xlsx data (2D array for the rows and columns)
*/
public function parseFile(string $filePath): array
{
$spreadsheet = IOFactory::load($filePath);
$worksheet = $spreadsheet->getActiveSheet();

$data = [];
foreach ($worksheet->getRowIterator() as $row) {
$rowData = [];
foreach ($row->getCellIterator() as $cell) {
$rowData[] = $cell->getValue();
}
$data[] = $rowData;
}
return $data;
}

/**
* checks partially if the header is correct
* @param array $header first entry returned from `ImportService->parseFile()`
* @return bool
*/
public function isHeaderValid(array $header): bool
{
// this header is partial because some fields are "dynamic", though this check should be deterministic enough.
$correctHeader = ["BNR", "Kurztitel", "Titel", "Listtyp", "Schulform", "Gegenstand", "Jahrgang", "Lehrerexemplar", "Anmerkung", "VNR", "Verlag", "Hauptbuch"];
$intersect = array_intersect($correctHeader, $header);
return count($intersect) === count($correctHeader);
}

public function persist(array $data)
{
}
}


12 changes: 12 additions & 0 deletions backend/symfony.lock
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@
"config/packages/lexik_jwt_authentication.yaml"
]
},
"nelmio/cors-bundle": {
"version": "2.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.5",
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
},
"files": [
"config/packages/nelmio_cors.yaml"
]
},
"phpunit/phpunit": {
"version": "9.6",
"recipe": {
Expand Down
Loading

0 comments on commit 6b72d85

Please sign in to comment.