Compare commits

...

3 Commits

Author SHA1 Message Date
6cde88bfe3 dockerize project 2024-12-04 14:40:38 +01:00
dfddaa5d0a wip ig 2024-11-15 20:04:29 +01:00
ed62eea84c add user and authenticator 2024-11-14 16:48:07 +01:00
26 changed files with 3432 additions and 9 deletions

View File

@ -7,12 +7,13 @@ xdebug_enabled: false
additional_hostnames: [] additional_hostnames: []
additional_fqdns: [] additional_fqdns: []
database: database:
type: mariadb type: postgres
version: "10.11" version: "17"
use_dns_when_possible: true use_dns_when_possible: true
composer_version: "2" composer_version: "2"
web_environment: [] web_environment: []
corepack_enable: false corepack_enable: false
host_db_port: "5432"
# Key features of DDEV's config.yaml: # Key features of DDEV's config.yaml:

18
.docker/Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM php:8.2-apache
RUN apt-get update \
&& apt-get install -qq -y --no-install-recommends \
cron \
nano \
locales coreutils apt-utils git libicu-dev g++ libpng-dev libxml2-dev libzip-dev libonig-dev libxslt-dev;
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
RUN curl -sSk https://getcomposer.org/installer | php -- --disable-tls && \
mv composer.phar /usr/local/bin/composer
RUN docker-php-ext-configure intl
RUN docker-php-ext-install pdo pdo_mysql mysqli gd opcache intl zip calendar dom mbstring zip gd xsl && a2enmod rewrite
RUN pecl install apcu && docker-php-ext-enable apcu
WORKDIR /var/www

10
.docker/compose.yaml Normal file
View File

@ -0,0 +1,10 @@
services:
www:
build: .
container_name: file-explorer
ports:
- "8741:80"
volumes:
- ./hosts:/etc/apache2/sites-enabled
- ../:/var/www/project
restart: always

18
.docker/hosts/server.conf Normal file
View File

@ -0,0 +1,18 @@
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/project/public
DirectoryIndex /index.php
<Directory /var/www/project/public>
AllowOverride None
Order Allow,Deny
Allow from All
</Directory>
# <Directory /var/www/project/public/bundles>
# FallbackResource disabled
# </Directory>
ErrorLog /var/log/apache2/project_error.log
CustomLog /var/log/apache2/project_access.log combined
</VirtualHost>

10
.env
View File

@ -18,3 +18,13 @@
APP_ENV=dev APP_ENV=dev
APP_SECRET=bfc9c288ee3dcce80dec8622c2870f27 APP_SECRET=bfc9c288ee3dcce80dec8622c2870f27
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://db:db@db:5432/db?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###

View File

@ -7,14 +7,20 @@
"php": ">=8.2", "php": ">=8.2",
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*", "ext-iconv": "*",
"doctrine/dbal": "^3",
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^3.3",
"symfony/console": "7.1.*", "symfony/console": "7.1.*",
"symfony/dotenv": "7.1.*", "symfony/dotenv": "7.1.*",
"symfony/flex": "^2", "symfony/flex": "^2",
"symfony/framework-bundle": "7.1.*", "symfony/framework-bundle": "7.1.*",
"symfony/runtime": "7.1.*", "symfony/runtime": "7.1.*",
"symfony/yaml": "7.1.*" "symfony/security-bundle": "7.1.*",
}, "symfony/twig-bundle": "7.1.*",
"require-dev": { "symfony/yaml": "7.1.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {
@ -64,5 +70,10 @@
"allow-contrib": false, "allow-contrib": false,
"require": "7.1.*" "require": "7.1.*"
} }
},
"require-dev": {
"symfony/maker-bundle": "^1.61",
"symfony/stopwatch": "7.1.*",
"symfony/web-profiler-bundle": "7.1.*"
} }
} }

2905
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,4 +2,11 @@
return [ return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
]; ];

View File

@ -0,0 +1,54 @@
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '16'
profiling_collect_backtrace: '%kernel.debug%'
use_savepoints: true
orm:
auto_generate_proxy_classes: true
enable_lazy_ghost_objects: true
report_fields_where_declared: true
validate_xml_mapping: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
identity_generation_preferences:
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
auto_mapping: true
mappings:
App:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
controller_resolver:
auto_mapping: false
when@test:
doctrine:
dbal:
# "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
when@prod:
doctrine:
orm:
auto_generate_proxy_classes: false
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system

View File

@ -0,0 +1,6 @@
doctrine_migrations:
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: false

View File

@ -0,0 +1,46 @@
security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
custom_authenticators:
- App\Security\Authenticator
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: /, roles: ROLE_USER}
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
when@test:
security:
password_hashers:
# By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes
# are not important, waste resources and increase test times. The following
# reduces the work factor to the lowest possible values.
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon

View File

@ -0,0 +1,6 @@
twig:
file_name_pattern: '*.twig'
when@test:
twig:
strict_variables: true

View File

@ -0,0 +1,17 @@
when@dev:
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler:
only_exceptions: false
collect_serializer_data: true
when@test:
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }

View File

@ -0,0 +1,3 @@
_security_logout:
resource: security.route_loader.logout
type: service

View File

@ -0,0 +1,8 @@
when@dev:
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler

0
migrations/.gitignore vendored Normal file
View File

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,17 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class HomeController extends AbstractController
{
#[Route(path: '/', name: 'app_home', methods: Request::METHOD_GET)]
public function home(): Response
{
return $this->render('index.html.twig');
}
}

0
src/Entity/.gitignore vendored Normal file
View File

91
src/Entity/User.php Normal file
View File

@ -0,0 +1,91 @@
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
class User implements UserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180)]
private string $email = '';
#[ORM\Column(length: 255)]
private string $name = '';
#[ORM\Column(length: 255)]
private string $username = '';
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function getUserIdentifier(): string
{
return $this->email;
}
public function getRoles(): array
{
return ['ROLE_USER'];
}
public function eraseCredentials(): void
{
}
public function getName(): string
{
return $this->name;
}
public function setName(?string $name): static
{
$this->name = $name;
return $this;
}
public function getUsername(): string
{
return $this->username;
}
public function setUsername(string $username): static
{
$this->username = $username;
return $this;
}
public static function createFromRequest(Request $request): static
{
return (new self())
->setEmail($request->headers->get('x-authentik-email'))
->setName($request->headers->get('x-authentik-name'))
->setUsername($request->headers->get('x-authentik-username'));
}
}

0
src/Repository/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,34 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<User>
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
public function save(User $user): void
{
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
}
public function exists(string $email): bool
{
return $this->createQueryBuilder('u')
->select('COUNT(u.id)')
->where('u.email = :email')
->setParameter('email', $email)
->getQuery()
->getSingleScalarResult() === 1;
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace App\Security;
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class Authenticator extends AbstractAuthenticator
{
public function __construct(private readonly UserRepository $userRepository)
{
}
public function supports(Request $request): ?bool
{
return $request->headers->has('x-authentik-username')
&& $request->headers->has('x-authentik-email')
&& $request->headers->has('x-authentik-name');
}
public function authenticate(Request $request): Passport
{
$email = $request->headers->get('x-authentik-email');
$this->checkGroup($request);
$this->createUserIfNotExists($email, $request);
return new SelfValidatingPassport(new UserBadge($email), [new RememberMeBadge()]);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
dd($exception);
return new JsonResponse([$exception], Response::HTTP_UNAUTHORIZED);
}
private function createUserIfNotExists(string $email, Request $request): void
{
if ($this->userRepository->exists($email)) {
$user = User::createFromRequest($request);
$this->userRepository->save($user);
}
}
private function checkGroup(Request $request): void
{
$groups = explode('|', $request->headers->get('x-authentik-groups'));
if (!in_array('file-explorer user', $groups)) {
throw new \LogicException('You are not allowed to access this resource');
}
}
}

View File

@ -1,4 +1,31 @@
{ {
"doctrine/doctrine-bundle": {
"version": "2.13",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.13",
"ref": "8d96c0b51591ffc26794d865ba3ee7d193438a83"
},
"files": [
"config/packages/doctrine.yaml",
"src/Entity/.gitignore",
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "3.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "3.1",
"ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
},
"files": [
"config/packages/doctrine_migrations.yaml",
"migrations/.gitignore"
]
},
"symfony/console": { "symfony/console": {
"version": "7.1", "version": "7.1",
"recipe": { "recipe": {
@ -42,6 +69,15 @@
"src/Kernel.php" "src/Kernel.php"
] ]
}, },
"symfony/maker-bundle": {
"version": "1.61",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/routing": { "symfony/routing": {
"version": "7.1", "version": "7.1",
"recipe": { "recipe": {
@ -54,5 +90,47 @@
"config/packages/routing.yaml", "config/packages/routing.yaml",
"config/routes.yaml" "config/routes.yaml"
] ]
},
"symfony/security-bundle": {
"version": "7.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.4",
"ref": "2ae08430db28c8eb4476605894296c82a642028f"
},
"files": [
"config/packages/security.yaml",
"config/routes/security.yaml"
]
},
"symfony/twig-bundle": {
"version": "7.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.4",
"ref": "cab5fd2a13a45c266d45a7d9337e28dee6272877"
},
"files": [
"config/packages/twig.yaml",
"templates/base.html.twig"
]
},
"symfony/web-profiler-bundle": {
"version": "7.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.1",
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
},
"files": [
"config/packages/web_profiler.yaml",
"config/routes/web_profiler.yaml"
]
},
"twig/extra-bundle": {
"version": "v3.13.0"
} }
} }

16
templates/base.html.twig Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Files | {% block title %}{% endblock %}</title>
<link rel="icon" href="icon.png">
{% block stylesheets %}
{% endblock %}
{% block javascripts %}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,7 @@
{% extends 'base.html.twig' %}
{% block title %}Home{% endblock %}
{% block body %}
{{ app.user.username }}
{% endblock %}