From ce6a517be9df2366b5faecdb81d9f139b79fdbd7 Mon Sep 17 00:00:00 2001
From: nic <nic@ubuntu22.myguest.virtualbox.org>
Date: Thu, 1 Feb 2024 05:39:53 +0300
Subject: [PATCH] =?UTF-8?q?feat:=20=D0=9F=D0=BE=D1=84=D0=B8=D0=BA=D1=88?=
 =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=D0=B5=20?=
 =?UTF-8?q?=D0=B8=D0=B7=20=D0=B3=D0=B8=D1=82=D0=B0,=20=D0=B4=D0=BE=D0=B1?=
 =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BE=D0=B3=D1=80=D0=B0?=
 =?UTF-8?q?=D0=BD=D0=B8=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=20?=
 =?UTF-8?q?=D1=80=D0=BE=D0=BB=D1=8F=D0=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 creating.sql                                | 98 ++++++++++++---------
 demo.http                                   | 34 +++++--
 src/Authenticator/LoginAuthenticator.php    | 15 ++--
 src/Authenticator/RegisterAuthenticator.php | 92 +++++++++++++++++++
 src/Controller/ArticleController.php        |  2 +-
 src/Controller/CommentController.php        |  2 +-
 src/Controller/ExampleController.php        |  4 +-
 src/Controller/RegisterController.php       | 24 -----
 src/Controller/TagController.php            |  2 +-
 src/Controller/TopicController.php          |  2 +-
 src/Controller/UserController.php           |  8 +-
 src/Entity/Admission.php                    | 53 +++++++++++
 src/Entity/Article.php                      | 25 ++++--
 src/Entity/ArticleTag.php                   | 11 +--
 src/Entity/Comment.php                      |  7 +-
 src/Entity/Tag.php                          |  4 +-
 src/Entity/Topic.php                        | 41 +++++++--
 src/Entity/User.php                         |  2 +-
 src/Model/ArticleModel.php                  |  2 +-
 src/Model/ArticleResponseModel.php          |  2 +-
 src/Model/BaseModel.php                     |  7 ++
 src/Model/CommentModel.php                  |  4 +-
 src/Model/RegisterModel.php                 |  4 +-
 src/Model/TagModel.php                      |  3 +-
 src/Model/TestModel.php                     | 20 -----
 src/Model/TopicModel.php                    | 40 ++++++++-
 src/Model/TopicResponseModel.php            | 80 +++++++++++++++++
 src/Model/UserModel.php                     |  4 +-
 src/Service/ArticleService.php              | 27 ++++--
 src/Service/CommentService.php              |  6 +-
 src/Service/TopicService.php                | 41 ++++++++-
 src/Service/UserProvider.php                | 15 +---
 32 files changed, 501 insertions(+), 180 deletions(-)
 create mode 100644 src/Authenticator/RegisterAuthenticator.php
 delete mode 100755 src/Controller/RegisterController.php
 create mode 100644 src/Entity/Admission.php
 create mode 100755 src/Model/BaseModel.php
 delete mode 100755 src/Model/TestModel.php
 create mode 100644 src/Model/TopicResponseModel.php

diff --git a/creating.sql b/creating.sql
index 3144d5f..ff1c08f 100644
--- a/creating.sql
+++ b/creating.sql
@@ -1,74 +1,88 @@
 CREATE TABLE IF NOT EXISTS tag(
-    id INTEGER PRIMARY KEY AUTO_INCREMENT,
-    title VARCHAR(60) NOT NULL UNIQUE
-);
+                                  id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                  title VARCHAR(60) NOT NULL UNIQUE
+    );
 
 CREATE TABLE IF NOT EXISTS user(
-    id INTEGER PRIMARY KEY AUTO_INCREMENT,
-    username VARCHAR(120) NOT NULL UNIQUE ,
+                                   id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                   username VARCHAR(120) NOT NULL UNIQUE ,
     email VARCHAR(120) NOT NULL UNIQUE ,
-    password VARCHAR(60) NOT NULL ,
+    password VARCHAR(120) NOT NULL ,
     role VARCHAR(10),
     created DATETIME,
     changed DATETIME,
     CONSTRAINT role_enum CHECK ( role IN ('USER', 'MODERATOR', 'ADMIN') )
-);
+    );
 
 CREATE TABLE IF NOT EXISTS topic(
-    id INTEGER PRIMARY KEY AUTO_INCREMENT,
-    author_id INTEGER,
-    title VARCHAR(365) NOT NULL ,
+                                    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                    author_id INTEGER,
+                                    title VARCHAR(365) NOT NULL ,
+    type VARCHAR(8) NOT NULL ,
     created DATETIME,
     CONSTRAINT FOREIGN KEY (author_id)
-                                REFERENCES user(id)
-                                ON DELETE no action
-);
+    REFERENCES user(id)
+    ON DELETE no action,
+    CONSTRAINT type_check CHECK ( type IN ('PUBLIC', 'PRIVATE') )
+    );
 
 CREATE TABLE IF NOT EXISTS article(
-    id INTEGER PRIMARY KEY AUTO_INCREMENT,
-    author_id INTEGER ,
-    topic_id INTEGER ,
-    title VARCHAR(480) NOT NULL ,
+                                      id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                      author_id INTEGER ,
+                                      topic_id INTEGER ,
+                                      title VARCHAR(480) NOT NULL ,
     body VARCHAR(10000) ,
     created DATETIME ,
     changed DATETIME ,
     CONSTRAINT FOREIGN KEY (author_id)
-                                  REFERENCES user(id)
-                                  ON DELETE no action ,
+    REFERENCES user(id)
+    ON DELETE no action ,
     CONSTRAINT FOREIGN KEY (topic_id)
-                                  REFERENCES topic(id)
-                                  ON DELETE set null
-);
+    REFERENCES topic(id)
+    ON DELETE set null
+    );
 
 CREATE TABLE IF NOT EXISTS comment(
-    id INTEGER PRIMARY KEY AUTO_INCREMENT,
-    author_id INTEGER,
-    article_id INTEGER,
-    body VARCHAR(1000),
+                                      id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                      author_id INTEGER,
+                                      article_id INTEGER,
+                                      body VARCHAR(1000),
     created DATETIME,
     changed DATETIME,
     CONSTRAINT FOREIGN KEY (author_id)
-                                  REFERENCES user(id)
-                                  ON DELETE no action ,
+    REFERENCES user(id)
+    ON DELETE no action ,
     CONSTRAINT FOREIGN KEY (article_id)
-                                  REFERENCES article(id)
-                                  ON DELETE CASCADE
-);
+    REFERENCES article(id)
+    ON DELETE CASCADE
+    );
 
 CREATE TABLE IF NOT EXISTS article_tag(
-    id INTEGER PRIMARY KEY AUTO_INCREMENT,
-    article_id INTEGER,
-    tag_id INTEGER,
-    CONSTRAINT FOREIGN KEY (article_id)
-                                      REFERENCES article(id)
-                                      ON DELETE CASCADE ,
+                                          id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                          article_id INTEGER,
+                                          tag_id INTEGER,
+                                          CONSTRAINT FOREIGN KEY (article_id)
+    REFERENCES article(id)
+    ON DELETE CASCADE ,
     CONSTRAINT FOREIGN KEY (tag_id)
-                                      REFERENCES tag(id)
-                                      ON DELETE CASCADE
-);
+    REFERENCES tag(id)
+    ON DELETE CASCADE
+    );
+
+CREATE TABLE IF NOT EXISTS admission(
+                                        id INTEGER PRIMARY KEY AUTO_INCREMENT,
+                                        topic_id INTEGER,
+                                        user_id INTEGER,
+                                        CONSTRAINT FOREIGN KEY (topic_id)
+    REFERENCES topic(id)
+    ON DELETE CASCADE ,
+    CONSTRAINT FOREIGN KEY (user_id)
+    REFERENCES user(id)
+    ON DELETE CASCADE
+    );
 
 INSERT INTO user(username, email, password, role, created, changed)
-VALUES ('test', 'test@gmail.com', '12345', 'USER', '2022-12-27 08:26:49.219717', '2022-12-27 08:26:49.219717');
+VALUES ('test', 'test@gmail.com', '5994471ABB01112AFCC18159F6CC74B4F511B99806DA59B3CAF5A9C173CACFC5', 'USER', '2022-12-27 08:26:49.219717', '2022-12-27 08:26:49.219717');
 
 INSERT INTO user(username, email, password, role, created, changed)
-VALUES ('Admin', 'admin@gmail.com', '12345', 'ADMIN', '2022-12-27 08:26:49.219717', '2022-12-27 08:26:49.219717');
+VALUES ('Admin', 'admin@gmail.com', '5994471ABB01112AFCC18159F6CC74B4F511B99806DA59B3CAF5A9C173CACFC5', 'ADMIN', '2022-12-27 08:26:49.219717', '2022-12-27 08:26:49.219717');
diff --git a/demo.http b/demo.http
index ea1b918..5dfca27 100644
--- a/demo.http
+++ b/demo.http
@@ -28,12 +28,20 @@ Content-Type: application/json
   "password": "12345"
 }
 
+### LOGIN
+POST http://localhost:8800/login
+Content-Type: application/json
+
+{
+  "username": "test",
+  "password": "12345"
+}
+
 ### REGISTER
 POST http://localhost:8800/register
 Content-Type: application/json
 
 {
-  "id": 5,
   "username": "kphp",
   "email": "kphp@mail.com",
   "password": "12355"
@@ -68,11 +76,21 @@ Content-Type: application/json
   ]
 }
 
+### LOGIN MODER
+POST http://localhost:8800/login
+Content-Type: application/json
+
+{
+  "username": "Moder",
+  "password": "qwerty"
+}
+
+
 ### GET USER
-GET http://localhost:8800/admin/user/3
+GET http://localhost:8800/admin/user/4
 
 ### PUT USER
-PUT http://localhost:8800/admin/user/3
+PUT http://localhost:8800/admin/user/4
 Content-Type: application/json
 
 {
@@ -111,6 +129,10 @@ Content-Type: application/json
   "id": 0,
   "authorId": 2,
   "title": "KPHP - not cool!",
+  "type": "PRIVATE",
+  "acceptedUsers": [
+    1
+  ],
   "created": "2024-01-22 21:30:00"
 }
 
@@ -118,7 +140,7 @@ Content-Type: application/json
 GET http://localhost:8800/topic/1
 
 ### PUT TUPIC
-PUT http://localhost:8800/moderator/topic/2
+PUT http://localhost:8800/moderator/topic/1
 Content-Type: application/json
 
 {
@@ -134,7 +156,7 @@ POST http://localhost:8800/article
 Content-Type: application/json
 
 {
-  "id": 2,
+  "id": 1,
   "authorId": 2,
   "topicId": 1,
   "title": "KPHP",
@@ -151,7 +173,7 @@ Content-Type: application/json
 GET http://localhost:8800/article/1
 
 ### CHANGE ARTICLE
-PUT http://localhost:8800/article/3
+PUT http://localhost:8800/article/1
 Content-Type: application/json
 
 {
diff --git a/src/Authenticator/LoginAuthenticator.php b/src/Authenticator/LoginAuthenticator.php
index 744364a..184dde6 100755
--- a/src/Authenticator/LoginAuthenticator.php
+++ b/src/Authenticator/LoginAuthenticator.php
@@ -15,7 +15,7 @@ use Kaa\Component\Security\AuthenticatorInterface;
 use Kaa\Component\Security\Exception\SessionException;
 use Kaa\Component\Security\Session\SessionService;
 use Random\RandomException;
-use Symfony\Component\Filesystem\Exception\RuntimeException;
+use RuntimeException;
 use Throwable;
 
 class LoginAuthenticator implements AuthenticatorInterface
@@ -43,9 +43,9 @@ class LoginAuthenticator implements AuthenticatorInterface
     public function supports(Request $request): bool
     {
         $content = json_decode($request->getContent(), true);
-        return $request->getMethod() === 'POST' &&
-            array_key_exists('username', $content) &&
-            array_key_exists('password', $content);
+        return $request->getMethod() === 'POST'
+            && array_key_exists('username', $content)
+            && array_key_exists('password', $content);
     }
 
     /**
@@ -55,7 +55,10 @@ class LoginAuthenticator implements AuthenticatorInterface
     {
         $data = json_decode($request->getContent(), true);
         $user = $this->entityManager->findOneBy(User::class, ['username' => $data['username']]);
-        if (($data['username'] === $user->getUsername()) && ($data['password'] === $user->getPassword())) {
+        if (
+            ($data['username'] === $user->getUsername())
+            && (strtoupper(hash('sha256', $data['password'])) === $user->getPassword())
+        ) {
             return fn () => $this->userProvider->getUser($user->getIdentifier());
         }
         throw new RuntimeException('Incorrect username or password');
@@ -77,6 +80,6 @@ class LoginAuthenticator implements AuthenticatorInterface
 
     public function onAuthenticationFailure(Request $request, Throwable $throwable): ?Response
     {
-        return new JsonResponse('Incorrect login or password', 401);
+        return new JsonResponse('Incorrect username or password', 401);
     }
 }
diff --git a/src/Authenticator/RegisterAuthenticator.php b/src/Authenticator/RegisterAuthenticator.php
new file mode 100644
index 0000000..6735bab
--- /dev/null
+++ b/src/Authenticator/RegisterAuthenticator.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace App\Authenticator;
+
+use App\Model\UserModel;
+use App\Service\UserProvider;
+use DateTimeImmutable;
+use Exception;
+use Kaa\Component\Database\EntityManager\EntityManagerInterface;
+use Kaa\Component\HttpMessage\Exception\BadRequestException;
+use Kaa\Component\HttpMessage\Exception\NoContentException;
+use Kaa\Component\HttpMessage\Exception\SuspiciousOperationException;
+use Kaa\Component\HttpMessage\Request;
+use Kaa\Component\HttpMessage\Response\JsonResponse;
+use Kaa\Component\HttpMessage\Response\Response;
+use Kaa\Component\Security\AuthenticatorInterface;
+use Kaa\Component\Security\Exception\SessionException;
+use Kaa\Component\Security\Session\SessionService;
+use Random\RandomException;
+use Throwable;
+
+class RegisterAuthenticator implements AuthenticatorInterface
+{
+    private SessionService $sessionService;
+    private EntityManagerInterface $entityManager;
+
+    private UserProvider $userProvider;
+
+    public function __construct(
+        SessionService         $sessionService,
+        EntityManagerInterface $entityManager,
+        UserProvider           $userProvider
+    )
+    {
+        $this->sessionService = $sessionService;
+        $this->entityManager = $entityManager;
+        $this->userProvider = $userProvider;
+    }
+
+    /**
+     * @throws BadRequestException
+     * @throws SuspiciousOperationException
+     * @throws NoContentException
+     */
+    public function supports(Request $request): bool
+    {
+        $content = json_decode($request->getContent(), true);
+        return $request->getMethod() === 'POST'
+            && array_key_exists('username', $content)
+            && array_key_exists('password', $content)
+            && array_key_exists('email', $content);
+    }
+
+    /**
+     * @throws NoContentException
+     * @throws Exception
+     */
+    public function authenticate(Request $request): callable
+    {
+        $data = json_decode($request->getContent(), true);
+        $id = $this->userProvider->createUser(new UserModel(
+            0,
+            (string)$data['username'],
+            (string)$data['email'],
+            (string)$data['password'],
+            ['USER'],
+            new DateTimeImmutable(),
+            new DateTimeImmutable()
+        ));
+        return fn() => $this->userProvider->getUser((string)$id);
+    }
+
+    /**
+     * @throws SessionException
+     * @throws RandomException
+     */
+    public function onAuthenticationSuccess(Request $request, callable $getUser): ?Response
+    {
+        $cookie = $this->sessionService->writeSession($getUser());
+
+        $response = new Response('Success', 200);
+        $response->addCookie($cookie);
+
+        return $response;
+    }
+
+    public function onAuthenticationFailure(Request $request, Throwable $throwable): ?Response
+    {
+        return new JsonResponse('Something went wrong...', 400);
+    }
+}
+
diff --git a/src/Controller/ArticleController.php b/src/Controller/ArticleController.php
index 5d45bf5..76ba18a 100755
--- a/src/Controller/ArticleController.php
+++ b/src/Controller/ArticleController.php
@@ -58,7 +58,7 @@ class ArticleController
         User $user
     ): Response {
         $articleService->updateArticle($id, $articleModel, $user);
-        return new JsonResponse('Successful changed', status: 200);
+        return new JsonResponse('Successful changed', 200);
     }
 
     #[Get('/my')]
diff --git a/src/Controller/CommentController.php b/src/Controller/CommentController.php
index 93c12c7..6256c45 100755
--- a/src/Controller/CommentController.php
+++ b/src/Controller/CommentController.php
@@ -61,7 +61,7 @@ class CommentController
         User $user
     ): Response {
         $commentService->updateComment($id, $commentModel, $user);
-        return new JsonResponse('Successful changed', status: 200);
+        return new JsonResponse('Successful changed', 200);
     }
 
     #[Get('/my')]
diff --git a/src/Controller/ExampleController.php b/src/Controller/ExampleController.php
index 803ec31..b7d251e 100755
--- a/src/Controller/ExampleController.php
+++ b/src/Controller/ExampleController.php
@@ -11,7 +11,7 @@ class ExampleController
     #[Get('/test')]
     public function test(): JsonResponse
     {
-        return new JsonResponse('{"hame": "kolaya"}', 200, []);
+        return new JsonResponse('{"hame": "kolaya"}', 200);
     } # warning for incorrect json for parsing?
 
     #[Get('/test/{id}')]
@@ -19,6 +19,6 @@ class ExampleController
         #[MapRouteParameter]
         int $id
     ): JsonResponse {
-        return new JsonResponse("{'id': {$id}}", 200, []);
+        return new JsonResponse("{'id': {$id}}", 200);
     }
 }
diff --git a/src/Controller/RegisterController.php b/src/Controller/RegisterController.php
deleted file mode 100755
index 0c3d7dc..0000000
--- a/src/Controller/RegisterController.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace App\Controller;
-
-use App\Model\RegisterModel;
-use App\Service\UserProvider;
-use Kaa\Component\DependencyInjectionDecorator\Inject;
-use Kaa\Component\RequestMapperDecorator\AsJsonResponse;
-use Kaa\Component\RequestMapperDecorator\MapJsonPayload;
-use Kaa\Component\Router\Attribute\Post;
-
-class RegisterController
-{
-    #[Post('/register')]
-    #[AsJsonResponse]
-    public function register(
-        #[MapJsonPayload]
-        RegisterModel   $registerModel,
-        #[Inject]
-        UserProvider $userProvider
-    ): int {
-        return $userProvider->registerUser($registerModel);
-    }
-}
diff --git a/src/Controller/TagController.php b/src/Controller/TagController.php
index fc90a9b..08c640c 100755
--- a/src/Controller/TagController.php
+++ b/src/Controller/TagController.php
@@ -49,6 +49,6 @@ class TagController
         TagModel $tagModel
     ): JsonResponse {
         $tagService->updateTag($id, $tagModel);
-        return new JsonResponse('Successful changed', status: 200);
+        return new JsonResponse('Successful changed', 200);
     }
 }
diff --git a/src/Controller/TopicController.php b/src/Controller/TopicController.php
index f9290db..af24c71 100755
--- a/src/Controller/TopicController.php
+++ b/src/Controller/TopicController.php
@@ -50,7 +50,7 @@ class TopicController
         TopicService $topicService
     ): JsonResponse {
         $topicService->updateTopic($id, $topicModel);
-        return new JsonResponse('Successful changed', status: 200);
+        return new JsonResponse('Successful changed', 200);
     }
 
     #[Get('/topic/my')]
diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php
index d0c9321..279a976 100755
--- a/src/Controller/UserController.php
+++ b/src/Controller/UserController.php
@@ -37,7 +37,7 @@ class UserController
         #[CurrentUser]
         User $user,
         #[Inject]
-        UserProvider $userProvider,
+        UserProvider $userProvider
     ): UserModel {
         return $userProvider->getCurrentUser($user);
     }
@@ -66,9 +66,9 @@ class UserController
         try {
             $userProvider->updateUser($userModel, $id);
         } catch (Exception $exception) {
-            return new JsonResponse('Nothing changed', status: 200);
+            return new JsonResponse('Nothing changed', 200);
         }
-        return new JsonResponse('Successfully changed', status: 200);
+        return new JsonResponse('Successfully changed', 200);
     }
 
     #[Put('/user/me')]
@@ -85,6 +85,6 @@ class UserController
             ->setCreationDate($user->getCreationTime())
             ->setChangingDate(new DateTimeImmutable());
         $userProvider->updateUser($userModel, $user->getId());
-        return new JsonResponse('Successfully changed', status: 200);
+        return new JsonResponse('Successfully changed', 200);
     }
 }
diff --git a/src/Entity/Admission.php b/src/Entity/Admission.php
new file mode 100644
index 0000000..be4641d
--- /dev/null
+++ b/src/Entity/Admission.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Entity;
+
+use Kaa\Component\Database\Attribute as Db;
+use Kaa\Component\Database\EntityInterface;
+
+#[Db\Entity]
+abstract class Admission implements EntityInterface
+{
+    #[Db\Id]
+    #[Db\Column]
+    protected ?int $id = null;
+
+    #[Db\ManyToOne(Topic::class)]
+    protected Topic $topic;
+
+    #[Db\ManyToOne(User::class)]
+    protected User $user;
+
+    public function getId(): int
+    {
+        return $this->id ?? -1;
+    }
+
+    public function setId(int $id): Admission
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function getTopic(): Topic
+    {
+        return $this->topic;
+    }
+
+    public function setTopic(Topic $topic): Admission
+    {
+        $this->topic = $topic;
+        return $this;
+    }
+
+    public function getUser(): User
+    {
+        return $this->user;
+    }
+
+    public function setUser(User $user): Admission
+    {
+        $this->user = $user;
+        return $this;
+    }
+}
diff --git a/src/Entity/Article.php b/src/Entity/Article.php
index 74ae8fa..b9cbbfb 100755
--- a/src/Entity/Article.php
+++ b/src/Entity/Article.php
@@ -34,9 +34,13 @@ abstract class Article implements EntityInterface
     #[Db\OneToMany(ArticleTag::class, mappedBy: 'article')]
     protected array $tags = [];
 
+    /** @var Comment[] */
+    #[Db\OneToMany(Comment::class, mappedBy: 'article')]
+    protected array$comments = [];
+
     public function getId(): int
     {
-        return $this->id;
+        return $this->id ?? -1;
     }
 
     public function setId(int $id): Article
@@ -45,11 +49,6 @@ abstract class Article implements EntityInterface
         return $this;
     }
 
-    public function getAuthorId(): int
-    {
-        return $this->author->getId();
-    }
-
     public function getTopicId(): int
     {
         return $this->topic->getId();
@@ -135,4 +134,18 @@ abstract class Article implements EntityInterface
         $this->tags[] = $tag;
         return $this;
     }
+
+    /**
+     * @return Comment[]
+     */
+    public function getComments(): array
+    {
+        return $this->comments;
+    }
+
+    public function addComment(Comment $comment): Article
+    {
+        $this->comments[] = $comment;
+        return $this;
+    }
 }
diff --git a/src/Entity/ArticleTag.php b/src/Entity/ArticleTag.php
index f0fd05d..c9c1381 100755
--- a/src/Entity/ArticleTag.php
+++ b/src/Entity/ArticleTag.php
@@ -20,7 +20,7 @@ abstract class ArticleTag implements EntityInterface
 
     public function getId(): int
     {
-        return $this->id;
+        return $this->id ?? -1;
     }
 
     public function setId(int $id): ArticleTag
@@ -29,15 +29,6 @@ abstract class ArticleTag implements EntityInterface
         return $this;
     }
 
-    public function getArticleId(): int
-    {
-        return $this->article->getId();
-    }
-
-    public function getTagId(): int
-    {
-        return $this->tag->getId();
-    }
 
     public function getArticle(): Article
     {
diff --git a/src/Entity/Comment.php b/src/Entity/Comment.php
index 77d229e..bac27b9 100755
--- a/src/Entity/Comment.php
+++ b/src/Entity/Comment.php
@@ -30,7 +30,7 @@ abstract class Comment implements EntityInterface
 
     public function getId(): int
     {
-        return $this->id;
+        return $this->id ?? -1;
     }
 
     public function setId(int $id): Comment
@@ -39,11 +39,6 @@ abstract class Comment implements EntityInterface
         return $this;
     }
 
-    public function getAuthorId(): int
-    {
-        return $this->author->getId();
-    }
-
     public function getArticleId(): int
     {
         return $this->article->getId();
diff --git a/src/Entity/Tag.php b/src/Entity/Tag.php
index 3678c13..6c03e30 100755
--- a/src/Entity/Tag.php
+++ b/src/Entity/Tag.php
@@ -15,13 +15,13 @@ abstract class Tag implements EntityInterface
     #[Db\Column]
     protected string $title;
 
-    /** @var Article[] */
+    /** @var ArticleTag[] */
     #[Db\OneToMany(ArticleTag::class, mappedBy: 'tag')]
     protected array $articles = [];
 
     public function getId(): int
     {
-        return $this->id;
+        return $this->id ?? -1;
     }
 
     public function setId(int $id): Tag
diff --git a/src/Entity/Topic.php b/src/Entity/Topic.php
index c2aa5d4..0422222 100755
--- a/src/Entity/Topic.php
+++ b/src/Entity/Topic.php
@@ -5,6 +5,7 @@ namespace App\Entity;
 use DateTimeImmutable;
 use Kaa\Component\Database\Attribute as Db;
 use Kaa\Component\Database\EntityInterface;
+use Twig\Node\Expression\Binary\AddBinary;
 
 #[Db\Entity]
 abstract class Topic implements EntityInterface
@@ -19,12 +20,21 @@ abstract class Topic implements EntityInterface
     #[Db\Column]
     protected string $title;
 
+    #[Db\Column]
+    protected string $type;
+
+    /**
+     * @var Admission[]
+     */
+    #[Db\OneToMany(Admission::class, mappedBy: 'topic')]
+    protected array $acceptedUsers = [];
+
     #[Db\Column(type: Db\Type::DateTimeImmutable)]
     protected DateTimeImmutable $created;
 
     public function getId(): int
     {
-        return $this->id;
+        return $this->id ?? -1;
     }
 
     public function setId(int $id): Topic
@@ -33,11 +43,6 @@ abstract class Topic implements EntityInterface
         return $this;
     }
 
-    public function getAuthorId(): int
-    {
-        return $this->author->getId();
-    }
-
     public function getTitle(): string
     {
         return $this->title;
@@ -49,6 +54,30 @@ abstract class Topic implements EntityInterface
         return $this;
     }
 
+    public function getType(): string
+    {
+        return $this->type;
+    }
+
+    public function setType(string $type): Topic
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    /**
+     * @return Admission[]
+     */
+    public function getAcceptedUsers(): array
+    {
+        return $this->acceptedUsers;
+    }
+
+    public function addAcceptedUser(Admission $user): Topic
+    {
+        $this->acceptedUsers[] = $user;
+        return $this;
+    }
 
     public function getCreationTime(): DateTimeImmutable
     {
diff --git a/src/Entity/User.php b/src/Entity/User.php
index bec9574..4021afe 100755
--- a/src/Entity/User.php
+++ b/src/Entity/User.php
@@ -41,7 +41,7 @@ abstract class User implements EntityInterface, UserInterface
 
     public function getId(): int
     {
-        return $this->id;
+        return $this->id ?? -1;
     }
 
     public function setId(int $id): User
diff --git a/src/Model/ArticleModel.php b/src/Model/ArticleModel.php
index 095b90d..b88fb62 100755
--- a/src/Model/ArticleModel.php
+++ b/src/Model/ArticleModel.php
@@ -5,7 +5,7 @@ namespace App\Model;
 use DateTimeImmutable;
 use Kaa\Component\Validator\Assert;
 
-class ArticleModel
+class ArticleModel extends BaseModel
 {
     #[Assert\PositiveOrZero]
     private int $id;
diff --git a/src/Model/ArticleResponseModel.php b/src/Model/ArticleResponseModel.php
index df2a26f..de11880 100755
--- a/src/Model/ArticleResponseModel.php
+++ b/src/Model/ArticleResponseModel.php
@@ -6,7 +6,7 @@ namespace App\Model;
 use DateTimeImmutable;
 use Kaa\Component\Validator\Assert;
 
-class ArticleResponseModel
+class ArticleResponseModel extends BaseModel
 {
     #[Assert\PositiveOrZero]
     private int $id;
diff --git a/src/Model/BaseModel.php b/src/Model/BaseModel.php
new file mode 100755
index 0000000..b24359a
--- /dev/null
+++ b/src/Model/BaseModel.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace App\Model;
+
+class BaseModel
+{
+}
diff --git a/src/Model/CommentModel.php b/src/Model/CommentModel.php
index 538fd77..5b7ad93 100755
--- a/src/Model/CommentModel.php
+++ b/src/Model/CommentModel.php
@@ -5,7 +5,7 @@ namespace App\Model;
 use DateTimeImmutable;
 use Kaa\Component\Validator\Assert;
 
-class CommentModel
+class CommentModel extends BaseModel
 {
     #[Assert\PositiveOrZero]
     private int $id;
@@ -19,7 +19,7 @@ class CommentModel
     #[Assert\Length(max: 1000)]
     private string $body;
 
-    private string $created; # maybe something asserting?
+    private string $created;
 
     private string $changed;
 
diff --git a/src/Model/RegisterModel.php b/src/Model/RegisterModel.php
index f0092bc..812c007 100755
--- a/src/Model/RegisterModel.php
+++ b/src/Model/RegisterModel.php
@@ -4,7 +4,7 @@ namespace App\Model;
 
 use Kaa\Component\Validator\Assert;
 
-class RegisterModel
+class RegisterModel extends BaseModel
 {
     #[Assert\PositiveOrZero]
     private int $id;
@@ -22,7 +22,7 @@ class RegisterModel
         int $id,
         string $username,
         string $email,
-        string $password,
+        string $password
     ) {
         $this->id = $id;
         $this->username = $username;
diff --git a/src/Model/TagModel.php b/src/Model/TagModel.php
index e64391a..703a64e 100755
--- a/src/Model/TagModel.php
+++ b/src/Model/TagModel.php
@@ -4,7 +4,7 @@ namespace App\Model;
 
 use Kaa\Component\Validator\Assert;
 
-class TagModel
+class TagModel extends BaseModel
 {
     #[Assert\PositiveOrZero]
     private int $id;
@@ -12,6 +12,7 @@ class TagModel
     #[Assert\Length(max: 60)]
     private string $title;
 
+
     public function __construct(int $id, string $title)
     {
         $this->id = $id;
diff --git a/src/Model/TestModel.php b/src/Model/TestModel.php
deleted file mode 100755
index bbd959b..0000000
--- a/src/Model/TestModel.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace App\Model;
-
-use DateTimeImmutable;
-
-class TestModel
-{
-    private DateTimeImmutable $dt;
-
-    public function __construct(DateTimeImmutable $dt)
-    {
-        $this->dt = $dt;
-    }
-
-    public function getDt(): DateTimeImmutable
-    {
-        return $this->dt;
-    }
-}
diff --git a/src/Model/TopicModel.php b/src/Model/TopicModel.php
index 25997b5..beb65e4 100755
--- a/src/Model/TopicModel.php
+++ b/src/Model/TopicModel.php
@@ -5,7 +5,7 @@ namespace App\Model;
 use DateTimeImmutable;
 use Kaa\Component\Validator\Assert;
 
-class TopicModel
+class TopicModel extends BaseModel
 {
     #[Assert\PositiveOrZero]
     private int $id;
@@ -16,13 +16,32 @@ class TopicModel
     #[Assert\Length(max: 365)]
     private string $title;
 
-    private string $created; # maybe something asserting?
+    private string $type;
 
-    public function __construct(int $id, int $author_id, string $title, DateTimeImmutable $created)
-    {
+    /**
+     * @var int[]
+     */
+    private array $acceptedUsers;
+
+    private string $created;
+
+    /**
+     * @param int[] $acceptedUsers
+     */
+
+    public function __construct(
+        int $id,
+        int $author_id,
+        string $title,
+        string $type,
+        array $acceptedUsers,
+        DateTimeImmutable $created
+    ) {
         $this->id = $id;
         $this->authorId = $author_id;
         $this->title = $title;
+        $this->type = $type;
+        $this->acceptedUsers = $acceptedUsers;
         $this->created = $created->format('Y-m-d H:i:s');
     }
 
@@ -41,6 +60,19 @@ class TopicModel
         return $this->title;
     }
 
+    public function getType(): string
+    {
+        return $this->type;
+    }
+
+    /**
+     * @return int[]
+     */
+    public function getAcceptedUsers(): array
+    {
+        return $this->acceptedUsers;
+    }
+
     public function getCreationTime(): string
     {
         return $this->created;
diff --git a/src/Model/TopicResponseModel.php b/src/Model/TopicResponseModel.php
new file mode 100644
index 0000000..119a576
--- /dev/null
+++ b/src/Model/TopicResponseModel.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace App\Model;
+
+use DateTimeImmutable;
+use Kaa\Component\Validator\Assert;
+
+class TopicResponseModel extends BaseModel
+{
+    #[Assert\PositiveOrZero]
+    private int $id;
+
+    #[Assert\PositiveOrZero]
+    private int $authorId;
+
+    #[Assert\Length(max: 365)]
+    private string $title;
+
+    private string $type;
+
+    /**
+     * @var UserModel[]
+     */
+    private array $accepted_users;
+
+    private string $created;
+
+    /**
+     * @param UserModel[] $accepted_users
+     */
+
+    public function __construct(
+        int $id,
+        int $author_id,
+        string $title,
+        string $type,
+        array $accepted_users,
+        DateTimeImmutable $created
+    ) {
+        $this->id = $id;
+        $this->authorId = $author_id;
+        $this->title = $title;
+        $this->type = $type;
+        $this->accepted_users = $accepted_users;
+        $this->created = $created->format('Y-m-d H:i:s');
+    }
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    public function getAuthorId(): int
+    {
+        return $this->authorId;
+    }
+
+    public function getTitle(): string
+    {
+        return $this->title;
+    }
+
+    public function getType(): string
+    {
+        return $this->type;
+    }
+
+    /**
+     * @return UserModel[]
+     */
+    public function getAcceptedUsers(): array
+    {
+        return $this->accepted_users;
+    }
+
+    public function getCreationTime(): string
+    {
+        return $this->created;
+    }
+}
\ No newline at end of file
diff --git a/src/Model/UserModel.php b/src/Model/UserModel.php
index 8fcf861..cafecb0 100755
--- a/src/Model/UserModel.php
+++ b/src/Model/UserModel.php
@@ -6,7 +6,7 @@ use DateTimeImmutable;
 use Kaa\Component\Security\UserInterface;
 use Kaa\Component\Validator\Assert;
 
-class UserModel implements UserInterface
+class UserModel extends BaseModel implements UserInterface
 {
     #[Assert\PositiveOrZero]
     private int $id;
@@ -17,7 +17,7 @@ class UserModel implements UserInterface
     #[Assert\Email]
     private string $email;
 
-    #[Assert\Length(max: 60)]
+    #[Assert\Length(max: 120)]
     private string $password;
 
     private string $created;
diff --git a/src/Service/ArticleService.php b/src/Service/ArticleService.php
index bd57eb4..871ecdb 100755
--- a/src/Service/ArticleService.php
+++ b/src/Service/ArticleService.php
@@ -4,7 +4,6 @@ namespace App\Service;
 
 use App\Entity\Article;
 use App\Entity\ArticleTag;
-use App\Entity\Comment;
 use App\Entity\Tag;
 use App\Entity\Topic;
 use App\Entity\User;
@@ -35,6 +34,14 @@ class ArticleService
                 throw new RuntimeException("Article contains wrong tag id {$tag}");
             }
         }
+        $topic = $this->entityManager->find(Topic::class, $articleModel->getTopicId());
+        if (
+            ($topic->getType() === 'PRIVATE')
+            && !in_array('MODERATOR', $user->getRoles())
+            && !in_array($user->getId(), array_map(fn ($a) => $a->getUser()->getId(), $topic->getAcceptedUsers()))
+        ) {
+            throw new ForbiddenOverwriteException("You are not allow to write in this topic!");
+        }
         $article = $this->entityManager->new(Article::class)
             ->setAuthor($user)
             ->setTopic($this->entityManager->find(Topic::class, $articleModel->getTopicId()))
@@ -59,22 +66,24 @@ class ArticleService
         }
         $tags = [];
         foreach ($article->getTags() as $tag) {
-            $tagBd = $this->entityManager->find(Tag::class, $tag->getTagId());
+            $tagBd = $this->entityManager->find(Tag::class, $tag->getTag()->getId());
             $tags[] = new TagModel($tagBd->getId(), $tagBd->getTitle());
         }
-        $topicBd = $this->entityManager->find(Topic::class, $article->getTopicId());
+        $topicBd = $article->getTopic();
         $topic = new TopicModel(
             $topicBd->getId(),
-            $topicBd->getAuthorId(),
+            $topicBd->getAuthor()->getId(),
             $topicBd->getTitle(),
+            $topicBd->getType(),
+            $topicBd->getAcceptedUsers(),
             $topicBd->getCreationTime()
         );
         $comments = [];
-        $commentsBd = $this->entityManager->findBy(Comment::class, ['article' => $article->getId()]);
+        $commentsBd = $article->getComments();
         foreach ($commentsBd as $commentBd) {
             $comments[] = new CommentModel(
                 $commentBd->getId(),
-                $commentBd->getArticleId(),
+                $commentBd->getAuthor()->getId(),
                 $commentBd->getArticleId(),
                 $commentBd->getBody(),
                 $commentBd->getCreationTime(),
@@ -83,7 +92,7 @@ class ArticleService
         }
         return new ArticleResponseModel(
             $article->getId(),
-            $article->getAuthorId(),
+            $article->getAuthor()->getId(),
             $topic,
             $article->geTitle(),
             $article->getBody(),
@@ -100,11 +109,11 @@ class ArticleService
         if ($article === null) {
             throw new RuntimeException("Article with id=$id not found");
         }
-        if ($user->getId() !== $article->getAuthorId()) {
+        if ($user->getId() !== $article->getAuthor()->getId()) {
             throw new ForbiddenOverwriteException('You are not the author');
         }
         $tagIds = array_map(function (ArticleTag $t) {
-            return $t->getTagId();
+            return $t->getTag()->getId();
         }, $article->getTags());
         $tags = [];
         foreach ($tagIds as $tag) {
diff --git a/src/Service/CommentService.php b/src/Service/CommentService.php
index 73506a7..88d61e7 100755
--- a/src/Service/CommentService.php
+++ b/src/Service/CommentService.php
@@ -45,7 +45,7 @@ class CommentService
         }
         return new CommentModel(
             $comment->getId(),
-            $comment->getAuthorId(),
+            $comment->getAuthor()->getId(),
             $comment->getArticleId(),
             $comment->getBody(),
             $comment->getCreationTime(),
@@ -59,7 +59,7 @@ class CommentService
         if ($comment === null) {
             throw new RuntimeException("Comment with id=$id not found");
         }
-        if ($user->getId() !== $comment->getAuthorId()) {
+        if ($user->getId() !== $comment->getAuthor()->getId()) {
             throw new ForbiddenOverwriteException('You are not the author');
         }
         $comment
@@ -75,7 +75,7 @@ class CommentService
         foreach ($comments as $comment) {
             $commentsModels[] = new CommentModel(
                 $comment->getId(),
-                $comment->getAuthorId(),
+                $comment->getAuthor()->getId(),
                 $comment->getArticleId(),
                 $comment->getBody(),
                 $comment->getCreationTime(),
diff --git a/src/Service/TopicService.php b/src/Service/TopicService.php
index 7ef3dba..66908f5 100755
--- a/src/Service/TopicService.php
+++ b/src/Service/TopicService.php
@@ -2,6 +2,7 @@
 
 namespace App\Service;
 
+use App\Entity\Admission;
 use App\Entity\Topic;
 use App\Entity\User;
 use App\Model\MultipleTopicsModel;
@@ -25,10 +26,22 @@ class TopicService
      */
     public function createTopic(TopicModel $topicModel): int
     {
+        if ($topicModel->getType() === 'PRIVATE'){
+            foreach ($topicModel->getAcceptedUsers() as $user){
+                if (!$this->entityManager->find(User::class, $user))
+                    throw new RuntimeException("Topic contains wrong user with id={$user}");
+            }
+        }
         $topic = $this->entityManager->new(Topic::class)
             ->setAuthor($this->entityManager->find(User::class, $topicModel->getAuthorId()))
             ->setTitle($topicModel->getTitle())
+            ->setType($topicModel->getType())
             ->setCreationTime(new DateTimeImmutable($topicModel->getCreationTime()));
+        foreach ($topicModel->getAcceptedUsers() as $user){
+            $this->entityManager->new(Admission::class)
+                ->setTopic($topic)
+                ->setUser($this->entityManager->find(User::class, $user));
+        }
         $this->entityManager->flush();
         return not_null($topic->getId());
     }
@@ -41,8 +54,10 @@ class TopicService
         }
         return new TopicModel(
             $topic->getId(),
-            $topic->getAuthorId(),
+            $topic->getAuthor()->getId(),
             $topic->getTitle(),
+            $topic->getType(),
+            $topic->getAcceptedUsers(),
             $topic->getCreationTime()
         );
     }
@@ -56,9 +71,29 @@ class TopicService
         if ($topic === null) {
             throw new RuntimeException("Topic with id=$id not found");
         }
+        $userIds = array_map(function (Admission $t) {
+            return $t->getUser()->getId();
+        }, $topic->getAcceptedUsers());
+        $users = [];
+        foreach ($userIds as $userId) {
+            if (!in_array($userId, $topicModel->getAcceptedUsers())) {
+                $users[] = $userId;
+            }
+        }
+        foreach ($users as $userId) {
+            if (!$this->entityManager->find(User::class, $userId)) {
+                throw new RuntimeException("Topic contains wrong user id {$userId}");
+            }
+        }
         $topic
             ->setTitle($topicModel->getTitle())
+            ->setType($topicModel->getType())
             ->setCreationTime(new DateTimeImmutable($topicModel->getCreationTime()));
+        foreach ($users as $user) {
+            $this->entityManager->new(Admission::class)
+                ->setUser($this->entityManager->find(User::class, $user))
+                ->setTopic($topic);
+        }
         $this->entityManager->flush();
     }
 
@@ -69,8 +104,10 @@ class TopicService
         foreach ($topics as $topic) {
             $topicsModelArray[] = new TopicModel(
                 $topic->getId(),
-                $topic->getAuthorId(),
+                $topic->getAuthor()->getId(),
                 $topic->getTitle(),
+                $topic->getType(),
+                $topic->getAcceptedUsers(),
                 $topic->getCreationTime()
             );
         }
diff --git a/src/Service/UserProvider.php b/src/Service/UserProvider.php
index 263c6ea..aea3325 100755
--- a/src/Service/UserProvider.php
+++ b/src/Service/UserProvider.php
@@ -23,19 +23,6 @@ class UserProvider implements UserProviderInterface
         $this->entityManager = $entityManager;
     }
 
-    public function registerUser(RegisterModel $registerModel): int
-    {
-        $user = $this->entityManager->new(User::class)
-            ->setUsername($registerModel->getUsername())
-            ->setEmail($registerModel->getEmail())
-            ->setPassword($registerModel->getPassword())
-            ->setCreationTime(new DateTimeImmutable())
-            ->setChangingTime(new DateTimeImmutable())
-            ->setRole(['USER']);
-        $this->entityManager->flush();
-        return $user->getId();
-    }
-
     /**
      * @throws Exception
      */
@@ -44,7 +31,7 @@ class UserProvider implements UserProviderInterface
         $user = $this->entityManager->new(User::class)
             ->setUsername($userModel->getUsername())
             ->setEmail($userModel->getEmail())
-            ->setPassword($userModel->getPassword())
+            ->setPassword(strtoupper(hash('sha256', $userModel->getPassword())))
             ->setCreationTime(new DateTimeImmutable($userModel->getCreationDate()))
             ->setChangingTime(new DateTimeImmutable($userModel->getChangingDate()))
             ->setRole($userModel->getRoles());
-- 
GitLab