diff --git a/composer.json b/composer.json index 3f90470..344d016 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "symfony/dotenv": "6.0.*", "symfony/flex": "^2", "symfony/framework-bundle": "6.0.*", + "symfony/mailer": "6.0.*", "symfony/proxy-manager-bridge": "6.0.*", "symfony/runtime": "6.0.*", "symfony/security-bundle": "6.0.*", diff --git a/composer.lock b/composer.lock index cd37129..e0831df 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "911ebb327c8f03e8e4ed4a7ece22c851", + "content-hash": "964ef6e102454980ceed317c53895b8f", "packages": [ { "name": "doctrine/annotations", @@ -1365,6 +1365,74 @@ }, "time": "2021-11-05T11:11:14+00:00" }, + { + "name": "egulias/email-validator", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ee0db30118f661fb166bcffbf5d82032df484697" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ee0db30118f661fb166bcffbf5d82032df484697", + "reference": "ee0db30118f661fb166bcffbf5d82032df484697", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^1.2", + "php": ">=7.2", + "symfony/polyfill-intl-idn": "^1.15" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^8.5.8|^9.3.3", + "vimeo/psalm": "^4" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2021-10-11T09:18:27+00:00" + }, { "name": "friendsofphp/proxy-manager-lts", "version": "v1.0.7", @@ -3976,6 +4044,161 @@ ], "time": "2022-04-02T06:35:11+00:00" }, + { + "name": "symfony/mailer", + "version": "v6.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "706af6b3e99ebcbc639c9c664f5579aaa869409b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/706af6b3e99ebcbc639c9c664f5579aaa869409b", + "reference": "706af6b3e99ebcbc639c9c664f5579aaa869409b", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3", + "php": ">=8.0.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3" + }, + "conflict": { + "symfony/http-kernel": "<5.4" + }, + "require-dev": { + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/messenger": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-27T17:10:30+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "c1701e88ad0ca49fc6ad6cdf360bc0e1209fb5e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/c1701e88ad0ca49fc6ad6cdf360bc0e1209fb5e1", + "reference": "c1701e88ad0ca49fc6ad6cdf360bc0e1209fb5e1", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-12T16:11:42+00:00" + }, { "name": "symfony/password-hasher", "version": "v6.0.3", @@ -4129,6 +4352,93 @@ ], "time": "2021-11-23T21:10:46+00:00" }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "749045c69efb97c70d25d7463abba812e91f3a44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44", + "reference": "749045c69efb97c70d25d7463abba812e91f3a44", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-09-14T14:02:44+00:00" + }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.25.0", diff --git a/config/packages/mailer.yaml b/config/packages/mailer.yaml new file mode 100644 index 0000000..56a650d --- /dev/null +++ b/config/packages/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: '%env(MAILER_DSN)%' diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 1d850dd..53d30d9 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -18,12 +18,12 @@ security: provider: app_user_provider form_login: - login_path: /login - check_path: /login_check + login_path: login + check_path: login_check enable_csrf: true logout: - path: /logout - target: / + path: logout + target: login remember_me: secret: '%kernel.secret%' lifetime: 2419200 # 28 days in seconds diff --git a/src/Command/DaemonCommand.php b/src/Command/DaemonCommand.php index f096763..3296a66 100644 --- a/src/Command/DaemonCommand.php +++ b/src/Command/DaemonCommand.php @@ -20,7 +20,7 @@ class DaemonCommand extends Command protected $kernel; protected $doctrine; - public function __construct(KernelInterface $kernel, ManagerRegistry $doctrine) + public function __construct(KernelInterface $kernel, ManagerRegistry $doctrine) { $this->kernel = $kernel; $this->doctrine = $doctrine; diff --git a/src/Command/MailFailedRunsCommand.php b/src/Command/MailFailedRunsCommand.php index c0440fd..4bc2985 100644 --- a/src/Command/MailFailedRunsCommand.php +++ b/src/Command/MailFailedRunsCommand.php @@ -2,25 +2,36 @@ namespace App\Command; +use App\Entity\Job; +use App\Entity\User; use App\Repository\JobRepository; use App\Repository\UserRepository; +use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Mailer\Mailer; +use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; +use Twig\Environment; class MailFailedRunsCommand extends Command { protected static $defaultName = 'mail-failed-runs'; protected $kernel; + protected $doctrine; + protected $templating; + protected $mailer; - public function __construct(KernelInterface $kernel) + public function __construct(KernelInterface $kernel, ManagerRegistry $doctrine, Environment $templating, MailerInterface $mailer) { $this->kernel = $kernel; + $this->doctrine = $doctrine; + $this->templating = $templating; + $this->mailer = $mailer; parent::__construct(); } @@ -31,37 +42,37 @@ class MailFailedRunsCommand extends Command ->setHelp('This command will send emails to the users when jobs are failing'); } + /** + * @throws \Twig\Error\RuntimeError + * @throws \Twig\Error\SyntaxError + * @throws \Twig\Error\LoaderError + */ protected function execute(InputInterface $input, OutputInterface $output) { - $userRepo = new UserRepository($this->kernel->getDbCon()); - $runRepo = $this->getEntityManager()->getRepository(Run::class); + $userRepo = $this->doctrine->getRepository(User::class); + $jobRepo = $this->doctrine->getRepository(Job::class); $failedJobs = $jobRepo->getFailingJobs(); if(!empty($failedJobs)) { - $twig = new Twig($this->kernel); - $html = $twig->render('mail-failed-runs.html.twig', ['jobs' => $failedJobs]); - $transport = Transport::fromDsn($_ENV['MAILER_DSN']); - $mailer = new Mailer($transport); + $html = $this->templating->render('mail-failed-runs.html.twig', ['jobs' => $failedJobs]); $email = (new Email()) ->from($_ENV['MAILER_FROM']) ->subject('Some cronjobs are failing') ->html($html); - $recipients = $userRepo->getMailAddresses(); foreach ($recipients as $recipient) { $email->addTo($recipient); } - $mailer->send($email); + $this->mailer->send($email); $output->writeln('Message sent'); } - return Command::SUCCESS; } } \ No newline at end of file diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php index 4337551..d273e71 100644 --- a/src/Command/RunCommand.php +++ b/src/Command/RunCommand.php @@ -2,12 +2,11 @@ namespace App\Command; -use App\Repository\JobRepository; -use App\Repository\RunRepository; +use App\Entity\Job; +use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -15,10 +14,12 @@ class RunCommand extends Command { protected static $defaultName = 'run'; protected $kernel; + protected $doctrine; - public function __construct(KernelInterface $kernel) + public function __construct(KernelInterface $kernel, ManagerRegistry $doctrine) { $this->kernel = $kernel; + $this->doctrine = $doctrine; parent::__construct(); } @@ -32,7 +33,7 @@ class RunCommand extends Command protected function execute(InputInterface $input, OutputInterface $output) { - $runRepo = $this->getEntityManager()->getRepository(Run::class); + $jobRepo = $this->doctrine->getRepository(Job::class); $jobId = (int)$input->getArgument('jobid'); $jobRunning = $jobRepo->isLockedJob($jobId); if($jobRunning) { @@ -56,10 +57,10 @@ class RunCommand extends Command $jobRepo->setTempVar($jobId, 'consolerun', false); $output->write($result['output']); if($result['success']) { - $output->writeln('JobRepository succeeded with in ' . number_format($result['runtime'], 3) . 'secs with exitcode ' . $result['exitcode']); + $output->writeln('Job succeeded with in ' . number_format($result['runtime'], 3) . 'secs with exitcode ' . $result['exitcode']); return Command::SUCCESS; } else { - $output->writeln('JobRepository failed in ' . number_format($result['runtime'], 3) . 'secs with exitcode ' . $result['exitcode']); + $output->writeln('Job failed in ' . number_format($result['runtime'], 3) . 'secs with exitcode ' . $result['exitcode']); return Command::FAILURE; } } diff --git a/src/Repository/RunRepository.php b/src/Repository/RunRepository.php index 71b2e7e..0f3a0d7 100644 --- a/src/Repository/RunRepository.php +++ b/src/Repository/RunRepository.php @@ -4,6 +4,7 @@ namespace App\Repository; +use App\Entity\Job; use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityRepository; @@ -55,7 +56,7 @@ class RunRepository extends EntityRepository public function cleanupRuns(array $jobids, int $maxage = NULL): int { - $jobRepo = new JobRepository($this->dbcon); + $jobRepo = $this->getEntityManager()->getRepository(Job::class); $allJobs = $jobRepo->getAllJobs(true); if(empty($jobids)) { foreach($allJobs as $key=>$job) { diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index ced7c04..73a1b6e 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -9,61 +9,12 @@ use Doctrine\ORM\EntityRepository; class UserRepository extends EntityRepository { - - /** - * @param string $user - * @param string $password - * @param bool $autologin - * @return int|bool - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ - public function checkAuthentication(string $user, string $password, bool $autologin = false): int|bool - { - $userSql = "SELECT * from user WHERE email = :user"; - $userStmt = $this->getEntityManager()->getConnection()->prepare($userSql); - $userRslt = $userStmt->executeQuery([':user' => $user]); - if($user = $userRslt->fetchAssociative()) { - if($autologin) $password = $this->getPassFromAutologinToken($password); - - $password = hash($_ENV['HASHING_METHOD'], $password); - - if(password_verify($password, $user['password'])) { - return $user['id']; - } - } - return false; - } - - public function createAutologinToken($password): string - { - $time = time(); - $password = $password . substr($time, -7) ; - $encrypted = Secret::encrypt($password); - return base64_encode(json_encode(['time' => $time, 'password' => base64_encode($encrypted)])); - } - - public function getPassFromAutologinToken($token) { - $extracted = json_decode(base64_decode($token), true); - $encrypted = base64_decode($extracted['password']); - - $decrypted = Secret::decrypt($encrypted); - - return ( - (($extracted['time'] + $_ENV['COOKIE_LIFETIME']) > time()) && - substr($extracted['time'], -7) == substr($decrypted, -7) - ) - ? substr($decrypted, 0, -7) : null; - } - public function getMailAddresses() { - $emailSql = "SELECT email FROM user WHERE sendmail = 1"; - $emailStmt = $this->getEntityManager()->getConnection()->prepare($emailSql); - $emailRslt = $emailStmt->executeQuery(); + $users = $this->findBy(['sendmail' => 1]); $return = []; - foreach($emailRslt->fetchAllAssociative() as $email) { - $return[] = $email['email']; + foreach($users as $user) { + $return[] = $user->getEmail(); } return $return; } diff --git a/symfony.lock b/symfony.lock index e6d4dc9..4449b4a 100644 --- a/symfony.lock +++ b/symfony.lock @@ -74,6 +74,9 @@ "doctrine/sql-formatter": { "version": "1.1.2" }, + "egulias/email-validator": { + "version": "3.1.2" + }, "friendsofphp/proxy-manager-lts": { "version": "v1.0.7" }, @@ -228,6 +231,18 @@ "symfony/http-kernel": { "version": "v6.0.7" }, + "symfony/mailer": { + "version": "6.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "4.3", + "ref": "97a61eabb351d7f6cb7702039bcfe07fe9d7e03c" + }, + "files": [ + "config/packages/mailer.yaml" + ] + }, "symfony/maker-bundle": { "version": "1.40", "recipe": { @@ -237,6 +252,9 @@ "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" } }, + "symfony/mime": { + "version": "v6.0.8" + }, "symfony/monolog-bridge": { "version": "v6.0.3" }, @@ -258,6 +276,9 @@ "symfony/polyfill-intl-grapheme": { "version": "v1.25.0" }, + "symfony/polyfill-intl-idn": { + "version": "v1.25.0" + }, "symfony/polyfill-intl-normalizer": { "version": "v1.25.0" },