diff --git a/.gen/objs.txt b/.gen/objs.txt index 99ebfb11..899c4cea 100644 --- a/.gen/objs.txt +++ b/.gen/objs.txt @@ -1,7 +1,7 @@ src/views/register/view.hpp src/views/register/view.cpp +src/views/register/Responses.hpp src/views/register/Request.hpp -src/views/register/Request.cpp src/views/login/view.hpp src/views/login/view.cpp src/views/login/Responses.hpp @@ -38,8 +38,13 @@ src/models/user_type/serialize.cpp src/models/user_type/postgre.hpp src/models/user_type/parse.hpp src/models/user_type/parse.cpp +src/models/user_credentials/type.hpp +src/models/user_credentials/postgre.hpp +src/models/user_credentials/fwd.hpp src/models/user/type.hpp +src/models/user/serialize.hpp src/models/user/postgre.hpp +src/models/user/parse.hpp src/models/timestring/type.hpp src/models/timestring/serialize.hpp src/models/timestring/serialize.cpp @@ -100,14 +105,18 @@ src/http/legacy_handler_parsed.hpp src/http/handler_parsed.hpp src/http/ErrorV1.hpp src/helpers/lesson_converter.hpp +src/components/controllers/postgres/user/sql_queries.hpp src/components/controllers/postgres/user/fwd.hpp src/components/controllers/postgres/user/fwd.cpp src/components/controllers/postgres/user/controller.hpp src/components/controllers/postgres/user/controller.cpp +src/components/controllers/postgres/user/config_schema.hpp +src/components/controllers/postgres/token/sql_queries.hpp src/components/controllers/postgres/token/fwd.hpp src/components/controllers/postgres/token/fwd.cpp src/components/controllers/postgres/token/controller.hpp src/components/controllers/postgres/token/controller.cpp +src/components/controllers/postgres/lesson/sql_queries.hpp src/components/controllers/postgres/lesson/fwd.hpp src/components/controllers/postgres/lesson/fwd.cpp src/components/controllers/postgres/lesson/controller.hpp diff --git a/api/api.yaml b/api/api.yaml index b7ec7f6d..27a13332 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -1,8 +1,4 @@ openapi: 3.0.0 -servers: - # Added by API Auto Mocking Plugin - - description: SwaggerHub API Auto Mocking - url: https://virtserver.swaggerhub.com/SABUDILOVSKIY_1/CourseWork/1.0.0 info: description: This is a simple timetable API version: "1.0.0" diff --git a/api/types/UserV1.yaml b/api/types/UserV1.yaml new file mode 100644 index 00000000..1088f311 --- /dev/null +++ b/api/types/UserV1.yaml @@ -0,0 +1,9 @@ +type: object +required: + - id + - type +properties: + type: + $ref: 'UserTypeV1.yaml' + id: + $ref: 'Id.yaml' diff --git a/api/views/get-timetable/Request.yaml b/api/views/get-timetable/Request.yaml index 71592aa2..53efb5bb 100644 --- a/api/views/get-timetable/Request.yaml +++ b/api/views/get-timetable/Request.yaml @@ -6,7 +6,7 @@ properties: begin: $ref: '../../types/Timestamp.yaml' end: - ref: '../../types/Timestamp.yaml' + $ref: '../../types/Timestamp.yaml' days: type: array items: @@ -42,7 +42,7 @@ properties: group_types: type: array items: - type: '../../types/EducationType.yaml' + $ref: '../../types/EducationType.yaml' room_ids: type: array items: diff --git a/api/views/login/ResponseV1.yaml b/api/views/login/ResponseV1.yaml index e90284a2..0bff072d 100644 --- a/api/views/login/ResponseV1.yaml +++ b/api/views/login/ResponseV1.yaml @@ -1,5 +1,5 @@ properties: token: $ref: '../../types/AuthTokenV1.yaml' - user_type: - $ref: ../../types/UserTypeV1.yaml + user: + $ref: ../../types/UserV1.yaml diff --git a/configs/config_vars.yaml b/configs/config_vars.yaml index 6e9e1f9a..5c82b689 100644 --- a/configs/config_vars.yaml +++ b/configs/config_vars.yaml @@ -6,4 +6,8 @@ is_testing: false server-port: 8080 +root_id: 'dddddddd-dddd-dddd-dddd-dddddddddddd' +root_login: 'root' +root_password: 'secret-password' + dbconnection: 'postgresql://timetable_vsu_backend_user:password@localhost:5432/timetable_vsu_backend_db_1' diff --git a/configs/static_config.yaml.in b/configs/static_config.yaml.in index e7988b8c..0eb19082 100644 --- a/configs/static_config.yaml.in +++ b/configs/static_config.yaml.in @@ -70,7 +70,11 @@ components_manager: blocking_task_processor: fs-task-processor dns_resolver: async sync-start: true - user_controller: {} + user-controller: + root: + login: $root_login + password: $root_password + id: $root_id token_controller: {} lesson_details_controller : {} handler-ping: diff --git a/configs_testing/config_vars_testing.yaml b/configs_testing/config_vars_testing.yaml index cbdf8fb9..8d821c7d 100644 --- a/configs_testing/config_vars_testing.yaml +++ b/configs_testing/config_vars_testing.yaml @@ -6,5 +6,9 @@ is_testing: true server-port: 8080 +root_id: 'dddddddd-dddd-dddd-dddd-dddddddddddd' +root_login: 'root' +root_password: 'secret-password' + # timetable_vsu_backend_db_1 is the service name + _ + filename of the db_1.sql dbconnection: 'postgresql://testsuite:password@localhost:15433/timetable_vsu_backend_db_1' diff --git a/postgresql/data/initial_data_auth.sql b/postgresql/data/initial_data_auth.sql index becb87a0..684b6431 100644 --- a/postgresql/data/initial_data_auth.sql +++ b/postgresql/data/initial_data_auth.sql @@ -1,2 +1,29 @@ -INSERT INTO vsu_timetable.user(login, password) -VALUES('some_nickname', 'some_password'); +INSERT INTO vsu_timetable.user(login, password, id) +VALUES('some_user', 'user_password', '111111c7-9654-4814-b36b-7d39c1ddded2'); + +INSERT INTO vsu_timetable.faculty +(id, "name") +VALUES('999111c7-9654-4814-bbbb-7d39c1ddded2', ''); + +INSERT INTO vsu_timetable.department +(id, "name", id_faculty) +VALUES('555111c7-9654-4814-cccc-7d39c1ddded2', '', '999111c7-9654-4814-bbbb-7d39c1ddded2'); + +INSERT INTO vsu_timetable.teacher +(id, fio, bio, id_department) +VALUES('222111c7-9654-4814-dddd-7d39c1ddded2', '', '', '555111c7-9654-4814-cccc-7d39c1ddded2'); + +INSERT INTO vsu_timetable.user(login, password, id) +VALUES('some_teacher', 'teacher_password', '222111c7-9654-4814-b36b-7d39c1ddded2'); + +INSERT INTO vsu_timetable.teacher_link +(id, id_user, id_teacher) +VALUES('666111c7-9654-4814-b36b-7d39c1ddded2', '222111c7-9654-4814-b36b-7d39c1ddded2', '222111c7-9654-4814-dddd-7d39c1ddded2'); + + +INSERT INTO vsu_timetable.user(login, password, id) +VALUES('some_admin', 'admin_password', '333111c7-9654-4814-b36b-7d39c1ddded2'); + +INSERT INTO vsu_timetable."admin" +(id, id_user) +VALUES('000111c7-9654-4814-b36b-7d39c1ddded2', '333111c7-9654-4814-b36b-7d39c1ddded2'); diff --git a/postgresql/schemas/db_1/003_add_user.sql b/postgresql/schemas/db_1/003_add_user.sql new file mode 100644 index 00000000..9d320947 --- /dev/null +++ b/postgresql/schemas/db_1/003_add_user.sql @@ -0,0 +1,21 @@ +BEGIN; + +DROP TYPE IF EXISTS vsu_timetable.user_type CASCADE; +CREATE TYPE vsu_timetable.user_type AS +ENUM ('user','admin','teacher'); + +DROP TYPE IF EXISTS vsu_timetable.userV1; +CREATE TYPE vsu_timetable.userV1 AS +( + id uuid, + type vsu_timetable.user_type +); + +DROP TYPE IF EXISTS vsu_timetable.user_credentials; +CREATE TYPE vsu_timetable.user_credentials AS +( + login text, + password text +); + +COMMIT; diff --git a/postgresql/schemas/db_1/004_cascade_delete_on_token.sql b/postgresql/schemas/db_1/004_cascade_delete_on_token.sql new file mode 100644 index 00000000..c82b831b --- /dev/null +++ b/postgresql/schemas/db_1/004_cascade_delete_on_token.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE vsu_timetable.token DROP CONSTRAINT IF EXISTS user_fk CASCADE; +ALTER TABLE vsu_timetable.token ADD CONSTRAINT user_fk FOREIGN KEY (id_user) REFERENCES vsu_timetable."user" (id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE; + +COMMIT; diff --git a/redocly.yaml b/redocly.yaml index 17ba87c8..7a115ddb 100644 --- a/redocly.yaml +++ b/redocly.yaml @@ -14,6 +14,7 @@ rules: operation-operationId: off operation-summary: off security-defined: off + no-empty-servers: off theme: openapi: diff --git a/src/components/controllers/postgres/lesson/controller.cpp b/src/components/controllers/postgres/lesson/controller.cpp index 142defc4..86cf1f2d 100644 --- a/src/components/controllers/postgres/lesson/controller.cpp +++ b/src/components/controllers/postgres/lesson/controller.cpp @@ -20,108 +20,18 @@ #include "models/lesson_v1/type.hpp" #include "models/lesson_with_details/postgre.hpp" #include "models/subgroup/serialize.hpp" +#include "sql_queries.hpp" #include "utils/convert/drop_properties_ref.hpp" -namespace timetable_vsu_backend::components::controllers::postgres { -namespace { -const userver::storages::postgres::Query qGetLessonsByFilter(R"( - WITH lesson_info as (SELECT - l.id AS lesson_id, - l.begin AS lesson_begin, - l.end AS lesson_end, - l.number_lesson AS lesson_number, - l.type_lesson AS lesson_type, - l.type_week AS lesson_week_type, - l.subgroup AS lesson_subgroup, - l.day AS lesson_day, - r.id AS room_id, - r.name AS room_name, - s.id AS subject_id, - s.name AS subject_name, - gs.id AS group_stage_id, - gs.begin AS group_stage_begin, - gs.end AS group_stage_end, - gs.course AS group_stage_course, - g.id AS group_id, - g.name AS group_name, - g.type AS group_type, - f.id AS faculty_id, - f.name AS faculty_name, - d.id AS department_id, - d.name AS department_name, - t.id AS teacher_id, - t.fio AS teacher_fio, - t.bio AS teacher_bio - FROM vsu_timetable.lesson AS l - LEFT JOIN vsu_timetable.room AS r ON l.id_room = r.id - LEFT JOIN vsu_timetable.shedule AS sh ON l.id_shedule = sh.id - LEFT JOIN vsu_timetable.subject AS s ON sh.id_subject = s.id - LEFT JOIN vsu_timetable.group_stage AS gs ON sh.id_group_stage = gs.id - LEFT JOIN vsu_timetable.group AS g ON gs.id_group = g.id - LEFT JOIN vsu_timetable.teacher AS t ON sh.id_teacher = t.id - LEFT JOIN vsu_timetable.department AS d ON t.id_department = d.id - LEFT JOIN vsu_timetable.faculty AS f ON d.id_faculty = f.id - ) - SELECT - lesson_id, - lesson_begin, - lesson_end, - lesson_number, - lesson_type, - lesson_week_type, - lesson_subgroup, - lesson_day, - room_id, - room_name, - subject_id, - subject_name, - group_stage_id AS group_id, - group_stage_course AS group_course, - group_name, - group_type, - faculty_id, - faculty_name, - department_id, - department_name, - teacher_id, - teacher_fio, - teacher_bio - FROM lesson_info - WHERE - ($1.lesson_ids IS null OR lesson_id = ANY($1.lesson_ids)) and - ($1.begin IS null OR $1.begin <= lesson_end) and - ($1."end" IS null OR $1."end" >= lesson_begin) and - ($1.days IS null OR lesson_day = ANY($1.days)) and - ($1.department_ids IS null OR department_id = ANY($1.department_ids)) and - ($1.department_names IS null OR department_name = ANY($1.department_names)) and - ($1.faculty_ids IS null OR faculty_id = ANY($1.faculty_ids)) and - ($1.faculty_names IS null OR faculty_name = ANY($1.faculty_names)) and - ($1.group_ids IS null OR group_stage_id = ANY($1.group_ids)) and - ($1.group_names IS null OR group_name = ANY($1.group_names)) and - ($1.group_courses is null OR group_stage_course = ANY($1.group_courses)) and - ($1.group_types is null OR group_type = ANY($1.group_types)) and - ($1.room_ids IS null OR room_id = ANY($1.room_ids)) and - ($1.room_names IS null OR room_name = ANY($1.room_names)) and - ($1.subject_names IS null OR subject_name = ANY($1.subject_names)) and - ($1.subject_ids IS null OR subject_id = ANY($1.subject_ids)) and - ($1.teacher_fios IS null OR teacher_fio = ANY($1.teacher_fios)) and - ($1.teacher_bios IS null OR teacher_bio = ANY($1.teacher_bios)) and - ($1.teacher_ids IS null OR teacher_id = ANY($1.teacher_ids)) and - ($1.subgroup IS null OR lesson_subgroup = $1.subgroup) and - ($1.week IS null OR lesson_week_type = $1.week) and - ($1.lesson_type is null OR lesson_type = $1.lesson_type) and - ($1.numbers is null OR lesson_number = ANY($1.numbers)) - ; - )"); -} +namespace timetable_vsu_backend::components::controllers::postgres::lesson { -std::vector LessonDetailsController::Search( +std::vector Controller::Search( const std::optional& filter) const { std::optional filter_tuple = convert::DropPropertiesToConstRefs(filter); auto result = pg_cluster_->Execute( userver::storages::postgres::ClusterHostType::kMaster, - qGetLessonsByFilter, filter_tuple); + sql::qGetLessonsByFilter, filter_tuple); std::vector lessons; lessons.reserve(result.Size()); auto it = result.begin(); @@ -132,12 +42,11 @@ std::vector LessonDetailsController::Search( } return lessons; } -LessonDetailsController::LessonDetailsController( - const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& context) +Controller::Controller(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& context) : LoggableComponentBase(config, context), pg_cluster_( context.FindComponent("postgres-db-1") .GetCluster()) { } -} // namespace timetable_vsu_backend::components::controllers::postgres +} // namespace timetable_vsu_backend::components::controllers::postgres::lesson diff --git a/src/components/controllers/postgres/lesson/controller.hpp b/src/components/controllers/postgres/lesson/controller.hpp index 61943a88..6ef944b3 100644 --- a/src/components/controllers/postgres/lesson/controller.hpp +++ b/src/components/controllers/postgres/lesson/controller.hpp @@ -9,20 +9,18 @@ #include "models/lesson_v1/type.hpp" #include "models/lesson_with_details/type.hpp" -namespace timetable_vsu_backend::components::controllers::postgres { -class LessonDetailsController final - : public userver::components::LoggableComponentBase { +namespace timetable_vsu_backend::components::controllers::postgres::lesson { +class Controller final : public userver::components::LoggableComponentBase { public: using userver::components::LoggableComponentBase::LoggableComponentBase; static constexpr inline std::string_view kName = "lesson_details_controller"; std::vector Search( const std::optional& filter) const; - LessonDetailsController( - const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& context); + Controller(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& context); protected: userver::storages::postgres::ClusterPtr pg_cluster_; }; -} // namespace timetable_vsu_backend::components::controllers::postgres +} // namespace timetable_vsu_backend::components::controllers::postgres::lesson diff --git a/src/components/controllers/postgres/lesson/fwd.cpp b/src/components/controllers/postgres/lesson/fwd.cpp index ee4d4289..7d2afa92 100644 --- a/src/components/controllers/postgres/lesson/fwd.cpp +++ b/src/components/controllers/postgres/lesson/fwd.cpp @@ -7,6 +7,6 @@ namespace timetable_vsu_backend::components::controllers::postgres { void AppendLessonDetailsController( userver::components::ComponentList& component_list) { - component_list.Append(); + component_list.Append(); } } // namespace timetable_vsu_backend::components::controllers::postgres diff --git a/src/components/controllers/postgres/lesson/fwd.hpp b/src/components/controllers/postgres/lesson/fwd.hpp index 99e01bfd..178cbba7 100644 --- a/src/components/controllers/postgres/lesson/fwd.hpp +++ b/src/components/controllers/postgres/lesson/fwd.hpp @@ -2,7 +2,9 @@ #include "utils/component_list_fwd.hpp" namespace timetable_vsu_backend::components::controllers::postgres { -class LessonDetailsController; +namespace lesson { +class Controller; +} void AppendLessonDetailsController( userver::components::ComponentList& component_list); } // namespace timetable_vsu_backend::components::controllers::postgres diff --git a/src/components/controllers/postgres/lesson/sql_queries.hpp b/src/components/controllers/postgres/lesson/sql_queries.hpp new file mode 100644 index 00000000..a49573c6 --- /dev/null +++ b/src/components/controllers/postgres/lesson/sql_queries.hpp @@ -0,0 +1,95 @@ +#pragma once +#include + +namespace timetable_vsu_backend::components::controllers::postgres::lesson:: + sql { +const userver::storages::postgres::Query qGetLessonsByFilter(R"( + WITH lesson_info as (SELECT + l.id AS lesson_id, + l.begin AS lesson_begin, + l.end AS lesson_end, + l.number_lesson AS lesson_number, + l.type_lesson AS lesson_type, + l.type_week AS lesson_week_type, + l.subgroup AS lesson_subgroup, + l.day AS lesson_day, + r.id AS room_id, + r.name AS room_name, + s.id AS subject_id, + s.name AS subject_name, + gs.id AS group_stage_id, + gs.begin AS group_stage_begin, + gs.end AS group_stage_end, + gs.course AS group_stage_course, + g.id AS group_id, + g.name AS group_name, + g.type AS group_type, + f.id AS faculty_id, + f.name AS faculty_name, + d.id AS department_id, + d.name AS department_name, + t.id AS teacher_id, + t.fio AS teacher_fio, + t.bio AS teacher_bio + FROM vsu_timetable.lesson AS l + LEFT JOIN vsu_timetable.room AS r ON l.id_room = r.id + LEFT JOIN vsu_timetable.shedule AS sh ON l.id_shedule = sh.id + LEFT JOIN vsu_timetable.subject AS s ON sh.id_subject = s.id + LEFT JOIN vsu_timetable.group_stage AS gs ON sh.id_group_stage = gs.id + LEFT JOIN vsu_timetable.group AS g ON gs.id_group = g.id + LEFT JOIN vsu_timetable.teacher AS t ON sh.id_teacher = t.id + LEFT JOIN vsu_timetable.department AS d ON t.id_department = d.id + LEFT JOIN vsu_timetable.faculty AS f ON d.id_faculty = f.id + ) + SELECT + lesson_id, + lesson_begin, + lesson_end, + lesson_number, + lesson_type, + lesson_week_type, + lesson_subgroup, + lesson_day, + room_id, + room_name, + subject_id, + subject_name, + group_stage_id AS group_id, + group_stage_course AS group_course, + group_name, + group_type, + faculty_id, + faculty_name, + department_id, + department_name, + teacher_id, + teacher_fio, + teacher_bio + FROM lesson_info + WHERE + ($1.lesson_ids IS null OR lesson_id = ANY($1.lesson_ids)) and + ($1.begin IS null OR $1.begin <= lesson_end) and + ($1."end" IS null OR $1."end" >= lesson_begin) and + ($1.days IS null OR lesson_day = ANY($1.days)) and + ($1.department_ids IS null OR department_id = ANY($1.department_ids)) and + ($1.department_names IS null OR department_name = ANY($1.department_names)) and + ($1.faculty_ids IS null OR faculty_id = ANY($1.faculty_ids)) and + ($1.faculty_names IS null OR faculty_name = ANY($1.faculty_names)) and + ($1.group_ids IS null OR group_stage_id = ANY($1.group_ids)) and + ($1.group_names IS null OR group_name = ANY($1.group_names)) and + ($1.group_courses is null OR group_stage_course = ANY($1.group_courses)) and + ($1.group_types is null OR group_type = ANY($1.group_types)) and + ($1.room_ids IS null OR room_id = ANY($1.room_ids)) and + ($1.room_names IS null OR room_name = ANY($1.room_names)) and + ($1.subject_names IS null OR subject_name = ANY($1.subject_names)) and + ($1.subject_ids IS null OR subject_id = ANY($1.subject_ids)) and + ($1.teacher_fios IS null OR teacher_fio = ANY($1.teacher_fios)) and + ($1.teacher_bios IS null OR teacher_bio = ANY($1.teacher_bios)) and + ($1.teacher_ids IS null OR teacher_id = ANY($1.teacher_ids)) and + ($1.subgroup IS null OR lesson_subgroup = $1.subgroup) and + ($1.week IS null OR lesson_week_type = $1.week) and + ($1.lesson_type is null OR lesson_type = $1.lesson_type) and + ($1.numbers is null OR lesson_number = ANY($1.numbers)) + ; + )"); +} diff --git a/src/components/controllers/postgres/token/controller.cpp b/src/components/controllers/postgres/token/controller.cpp index 9a99ac3d..f76816d9 100644 --- a/src/components/controllers/postgres/token/controller.cpp +++ b/src/components/controllers/postgres/token/controller.cpp @@ -16,32 +16,20 @@ #include "models/user/postgre.hpp" #include "models/user_type/postgre.hpp" +#include "sql_queries.hpp" -namespace { -const userver::storages::postgres::Query qGetUserByTokenId(R"( - WITH found_token AS (select id_user - from vsu_timetable.token WHERE id = $1 AND expire_time > $2) - SELECT id, login, password, user_type from vsu_timetable."user" LEFT OUTER JOIN found_token ON id_user = "user".id - )"), - qAddToken(R"( - insert into vsu_timetable."token" (id_user, expire_time) values ($1, $2) RETURNING id - )"); -} - -namespace timetable_vsu_backend::components::controllers::postgres { -TokenController::TokenController( - const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& context) +namespace timetable_vsu_backend::components::controllers::postgres::token { +Controller::Controller(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& context) : LoggableComponentBase(config, context), pg_cluster_( context.FindComponent("postgres-db-1") .GetCluster()) { } -std::optional TokenController::GetById( - std::string_view id) const { +std::optional Controller::GetById(std::string_view id) const { auto result = pg_cluster_->Execute( userver::storages::postgres::ClusterHostType::kMaster, - qGetUserByTokenId, id, userver::utils::datetime::Now()); + sql::qGetUserByTokenId, id, userver::utils::datetime::Now()); if (result.IsEmpty()) { return std::nullopt; } @@ -49,17 +37,17 @@ std::optional TokenController::GetById( userver::storages::postgres::kRowTag); } -std::optional TokenController::CreateNew( - const models::User::Id& id, +std::optional Controller::CreateNew( + const boost::uuids::uuid& user_id, const std::chrono::system_clock::time_point& time) const { LOG_DEBUG() << fmt::format("Try to create new token, id_user: {}", - boost::uuids::to_string(id.GetUnderlying())); + boost::uuids::to_string(user_id)); auto result = pg_cluster_->Execute( - userver::storages::postgres::ClusterHostType::kMaster, qAddToken, id, - time); + userver::storages::postgres::ClusterHostType::kMaster, sql::qAddToken, + user_id, time); if (result.IsEmpty()) { return {}; } return result.AsSingleRow(); } -} // namespace timetable_vsu_backend::components::controllers::postgres +} // namespace timetable_vsu_backend::components::controllers::postgres::token diff --git a/src/components/controllers/postgres/token/controller.hpp b/src/components/controllers/postgres/token/controller.hpp index 5233705f..84aaa37b 100644 --- a/src/components/controllers/postgres/token/controller.hpp +++ b/src/components/controllers/postgres/token/controller.hpp @@ -6,21 +6,20 @@ #include "models/user/type.hpp" -namespace timetable_vsu_backend::components::controllers::postgres { +namespace timetable_vsu_backend::components::controllers::postgres::token { -class TokenController final - : public userver::components::LoggableComponentBase { +class Controller final : public userver::components::LoggableComponentBase { public: using userver::components::LoggableComponentBase::LoggableComponentBase; static constexpr inline std::string_view kName = "token_controller"; std::optional GetById(std::string_view id) const; std::optional CreateNew( - const models::User::Id& id, + const boost::uuids::uuid& user_id, const std::chrono::system_clock::time_point& time) const; - TokenController(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& context); + Controller(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& context); protected: userver::storages::postgres::ClusterPtr pg_cluster_; }; -} // namespace timetable_vsu_backend::components::controllers::postgres +} // namespace timetable_vsu_backend::components::controllers::postgres::token diff --git a/src/components/controllers/postgres/token/fwd.cpp b/src/components/controllers/postgres/token/fwd.cpp index 79061967..4e8f2916 100644 --- a/src/components/controllers/postgres/token/fwd.cpp +++ b/src/components/controllers/postgres/token/fwd.cpp @@ -6,6 +6,6 @@ namespace timetable_vsu_backend::components::controllers::postgres { void AppendTokenController(userver::components::ComponentList& component_list) { - component_list.Append(); + component_list.Append(); } } // namespace timetable_vsu_backend::components::controllers::postgres diff --git a/src/components/controllers/postgres/token/fwd.hpp b/src/components/controllers/postgres/token/fwd.hpp index b83628bd..20d07cce 100644 --- a/src/components/controllers/postgres/token/fwd.hpp +++ b/src/components/controllers/postgres/token/fwd.hpp @@ -1,6 +1,8 @@ #pragma once #include "utils/component_list_fwd.hpp" namespace timetable_vsu_backend::components::controllers::postgres { -class TokenController; +namespace token { +class Controller; +} void AppendTokenController(userver::components::ComponentList& component_list); } // namespace timetable_vsu_backend::components::controllers::postgres diff --git a/src/components/controllers/postgres/token/sql_queries.hpp b/src/components/controllers/postgres/token/sql_queries.hpp new file mode 100644 index 00000000..a86ef6a8 --- /dev/null +++ b/src/components/controllers/postgres/token/sql_queries.hpp @@ -0,0 +1,11 @@ +#include +namespace timetable_vsu_backend::components::controllers::postgres::token::sql { +const userver::storages::postgres::Query qGetUserByTokenId(R"( + WITH found_token AS (select id_user + from vsu_timetable.token WHERE id = $1 AND expire_time > $2) + SELECT id, login, password, user_type from vsu_timetable."user" LEFT OUTER JOIN found_token ON id_user = "user".id + )"), + qAddToken(R"( + insert into vsu_timetable."token" (id_user, expire_time) values ($1, $2) RETURNING id + )"); +} diff --git a/src/components/controllers/postgres/user/config_schema.hpp b/src/components/controllers/postgres/user/config_schema.hpp new file mode 100644 index 00000000..5726b6b8 --- /dev/null +++ b/src/components/controllers/postgres/user/config_schema.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +namespace timetable_vsu_backend::components::controllers::postgres::user:: + config { +const std::string schema = + R"( +type: object +description: Компонент, отвечающий за авторизацию и определение типа пользователей +additionalProperties: false +properties: + root: + type: object + description: Настройки суперпользователя. Он будет создан на время работы сервиса, а затем будет удален из БД. Логин и id будут освобождены для него. + additionalProperties: false + properties: + login: + type: string + description: Логин для суперпользователя + password: + type: string + description: Пароль для суперпользователя + id: + type: string + description: UUID суперпользователя +)"; +} diff --git a/src/components/controllers/postgres/user/controller.cpp b/src/components/controllers/postgres/user/controller.cpp index 5b290de6..3f081868 100644 --- a/src/components/controllers/postgres/user/controller.cpp +++ b/src/components/controllers/postgres/user/controller.cpp @@ -2,57 +2,102 @@ #include +#include #include +#include #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include "components/controllers/postgres/user/sql_queries.hpp" +#include "config_schema.hpp" #include "models/user/postgre.hpp" -#include "models/user_type/postgre.hpp" -namespace { -const userver::storages::postgres::Query qGetUserByLogin(R"( - select * from vsu_timetable."user" WHERE login = $1 - )"), - qAddUser(R"( - insert into vsu_timetable."user"(login, password) values ($1, $2) ON CONFLICT DO NOTHING - RETURNING id - )"); -} +#include "models/user/type.hpp" +#include "models/user_credentials/postgre.hpp" -namespace timetable_vsu_backend::components::controllers::postgres { -UserController::UserController( - const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& context) +namespace timetable_vsu_backend::components::controllers::postgres::user { +Controller::Controller(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& context) : LoggableComponentBase(config, context), pg_cluster_( context.FindComponent("postgres-db-1") .GetCluster()) { + models::UserCredentials root; + root.login() = config["root"]["login"].As(); + root.password() = config["root"]["password"].As(); + root_id = boost::lexical_cast( + config["root"]["id"].As()); + try { + InternalForceCreateUser(root_id.value(), root); + } catch (std::exception& exc) { + LOG_WARNING() << fmt::format("Error while creating superuser : {}", + exc.what()); + root_id = std::nullopt; + } } -std::optional UserController::GetByLogin( - std::string_view login) const { +void Controller::InternalForceCreateUser( + const boost::uuids::uuid&, + const models::UserCredentials& user_credentials) { + pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kMaster, + sql::qDropUserById, root_id); + pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kMaster, + sql::qDropUserByLogin, user_credentials.login()); + pg_cluster_->Execute( + userver::storages::postgres::ClusterHostType::kMaster, + sql::qInternalAddUser, root_id, + utils::convert::DropPropertiesToConstRefs(user_credentials)); +} + +std::optional Controller::GetByCredentials( + const models::UserCredentials& user_credentials) const { auto result = pg_cluster_->Execute( - userver::storages::postgres::ClusterHostType::kMaster, qGetUserByLogin, - login); + userver::storages::postgres::ClusterHostType::kMaster, sql::qGetUser, + utils::convert::DropPropertiesToConstRefs(user_credentials)); if (result.IsEmpty()) { return std::nullopt; } - return result.AsSingleRow( - userver::storages::postgres::kRowTag); + std::optional user = std::nullopt; + if (result.Size() == 1) { + models::User read; + auto pg_user = utils::convert::DropPropertiesToMutRefs(read); + result[0].To(pg_user, userver::storages::postgres::kRowTag); + user = read; + } else if (result.Size() != 0) { + LOG_WARNING() << fmt::format("Unexpected quantity from postgres: {}", + result.Size()); + } + if (user && user->id() == root_id) { + user->type() = models::UserType::kRoot; + } + return user; } -std::optional UserController::TryToAdd( - const models::User& user) const { +std::optional Controller::TryToAdd( + const models::UserCredentials& user_credentials) const { auto result = pg_cluster_->Execute( - userver::storages::postgres::ClusterHostType::kMaster, qAddUser, - user.login, user.password); + userver::storages::postgres::ClusterHostType::kMaster, sql::qAddUser, + utils::convert::DropPropertiesToConstRefs(user_credentials)); if (result.IsEmpty()) { return {}; } return result.AsSingleRow(); } -} // namespace timetable_vsu_backend::components::controllers::postgres +Controller::~Controller() { + pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kMaster, + sql::qDropUserById, root_id); +} + +userver::yaml_config::Schema Controller::GetStaticConfigSchema() { + return userver::yaml_config::MergeSchemas< + userver::components::LoggableComponentBase>(config::schema); +} +} // namespace timetable_vsu_backend::components::controllers::postgres::user diff --git a/src/components/controllers/postgres/user/controller.hpp b/src/components/controllers/postgres/user/controller.hpp index e30b9ed8..625b7abc 100644 --- a/src/components/controllers/postgres/user/controller.hpp +++ b/src/components/controllers/postgres/user/controller.hpp @@ -1,22 +1,32 @@ #pragma once +#include #include #include #include #include "models/user/type.hpp" +#include "models/user_credentials/fwd.hpp" -namespace timetable_vsu_backend::components::controllers::postgres { +namespace timetable_vsu_backend::components::controllers::postgres::user { -class UserController final : public userver::components::LoggableComponentBase { +class Controller final : public userver::components::LoggableComponentBase { public: using userver::components::LoggableComponentBase::LoggableComponentBase; - static constexpr inline std::string_view kName = "user_controller"; - std::optional GetByLogin(std::string_view login) const; - std::optional TryToAdd(const models::User& user) const; - UserController(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& context); + static constexpr inline std::string_view kName = "user-controller"; + void InternalForceCreateUser( + const boost::uuids::uuid&, + const models::UserCredentials& user_credentials); + std::optional GetByCredentials( + const models::UserCredentials& user_credentials) const; + std::optional TryToAdd( + const models::UserCredentials& user_credentials) const; + static userver::yaml_config::Schema GetStaticConfigSchema(); + Controller(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& context); + ~Controller(); protected: + std::optional root_id; userver::storages::postgres::ClusterPtr pg_cluster_; }; -} // namespace timetable_vsu_backend::components::controllers::postgres +} // namespace timetable_vsu_backend::components::controllers::postgres::user diff --git a/src/components/controllers/postgres/user/fwd.cpp b/src/components/controllers/postgres/user/fwd.cpp index 691ff5c7..6e3b8d98 100644 --- a/src/components/controllers/postgres/user/fwd.cpp +++ b/src/components/controllers/postgres/user/fwd.cpp @@ -6,6 +6,6 @@ namespace timetable_vsu_backend::components::controllers::postgres { void AppendUserController(userver::components::ComponentList& component_list) { - component_list.Append(); + component_list.Append(); } } // namespace timetable_vsu_backend::components::controllers::postgres diff --git a/src/components/controllers/postgres/user/fwd.hpp b/src/components/controllers/postgres/user/fwd.hpp index 1eb34ddb..b96fd786 100644 --- a/src/components/controllers/postgres/user/fwd.hpp +++ b/src/components/controllers/postgres/user/fwd.hpp @@ -1,6 +1,9 @@ #pragma once #include "utils/component_list_fwd.hpp" namespace timetable_vsu_backend::components::controllers::postgres { -class UserController; +namespace user { +class Controller; +} + void AppendUserController(userver::components::ComponentList& component_list); } // namespace timetable_vsu_backend::components::controllers::postgres diff --git a/src/components/controllers/postgres/user/sql_queries.hpp b/src/components/controllers/postgres/user/sql_queries.hpp new file mode 100644 index 00000000..8e407b2a --- /dev/null +++ b/src/components/controllers/postgres/user/sql_queries.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "userver/storages/postgres/query.hpp" + +namespace timetable_vsu_backend::components::controllers::postgres::user::sql { +const userver::storages::postgres::Query qGetUser(R"( + WITH all_user AS ( SELECT + u.id AS user_id, + a.id AS admin_id, + tl.id_teacher AS teacher_id + from vsu_timetable.user AS u + left join vsu_timetable.admin AS a on u.id = a.id_user + left join vsu_timetable.teacher_link AS tl on u.id = tl.id_user + where u.login = $1.login and u."password" = $1.password + ) + SELECT + user_id, + CASE + WHEN admin_id IS NOT NULL THEN 'admin'::vsu_timetable.user_type + WHEN teacher_id IS NOT NULL THEN 'teacher'::vsu_timetable.user_type + ELSE 'user' + END AS type + FROM all_user; + )"), + qAddUser(R"( + insert into vsu_timetable."user"(login, password) values ($1.login, $1.password) ON CONFLICT DO NOTHING + RETURNING id + )"), + qInternalAddUser(R"( + insert into vsu_timetable."user"(id, login, password) values ($1, $2.login, $2.password) + )"), + qDropUserByLogin(R"( + DELETE FROM vsu_timetable."user" WHERE login=$1; + )"), + qDropUserById(R"( + DELETE FROM vsu_timetable."user" WHERE id=$1; + )"); +} diff --git a/src/models/lesson_filter/postgre.hpp b/src/models/lesson_filter/postgre.hpp index fe240133..3e6f99ce 100644 --- a/src/models/lesson_filter/postgre.hpp +++ b/src/models/lesson_filter/postgre.hpp @@ -4,10 +4,15 @@ #include "models/lesson_filter/type.hpp" #include "utils/convert/drop_properties_ref.hpp" namespace timetable_vsu_backend::models { -using TupleLessonFilter = +using TupleLessonFilterRaw = timetable_vsu_backend::utils::convert::drop_properties_to_ref_const_t< LessonFilter>; -} +// using TupleLessonFilter = +// userver::utils::StrongTypedef; +using TupleLessonFilter = TupleLessonFilterRaw; +} // namespace timetable_vsu_backend::models + namespace userver::storages::postgres::io { template <> diff --git a/src/models/user/parse.hpp b/src/models/user/parse.hpp new file mode 100644 index 00000000..37de745d --- /dev/null +++ b/src/models/user/parse.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "models/user_type/parse.hpp" diff --git a/src/models/user/postgre.hpp b/src/models/user/postgre.hpp index 9491c70d..cb84b0c3 100644 --- a/src/models/user/postgre.hpp +++ b/src/models/user/postgre.hpp @@ -1,16 +1,24 @@ #pragma once -#include -#include -#include +#include +#include -#include "type.hpp" +#include "models/user/type.hpp" +#include "models/user_type/postgre.hpp" +#include "utils/convert/drop_properties_ref.hpp" +namespace timetable_vsu_backend::models { +using TupleUserRaw = + timetable_vsu_backend::utils::convert::drop_properties_to_ref_const_t; + +// using TupleUser = +// userver::utils::StrongTypedef; +using TupleUser = TupleUserRaw; +} // namespace timetable_vsu_backend::models namespace userver::storages::postgres::io { -// This specialization MUST go to the header together with the mapped type -using namespace timetable_vsu_backend::models; + template <> -struct CppToUserPg { - static constexpr userver::storages::postgres::DBTypeName postgres_name = - "uuid"; +struct CppToUserPg { + static constexpr DBTypeName postgres_name = "vsu_timetable.userV1"; }; } // namespace userver::storages::postgres::io diff --git a/src/models/user/serialize.hpp b/src/models/user/serialize.hpp new file mode 100644 index 00000000..dc0b06ed --- /dev/null +++ b/src/models/user/serialize.hpp @@ -0,0 +1,3 @@ +#pragma once +#include "models/user_type/serialize.hpp" +#include "utils/convert/json_serialize.hpp" diff --git a/src/models/user/type.hpp b/src/models/user/type.hpp index b2462c46..5a36abdb 100644 --- a/src/models/user/type.hpp +++ b/src/models/user/type.hpp @@ -2,11 +2,17 @@ #include #include +#include "models/user_type/type.hpp" +#include "utils/convert/base.hpp" + namespace timetable_vsu_backend::models { +namespace convert = utils::convert; struct User { - using Id = userver::utils::StrongTypedef; - Id id; - std::string login; - std::string password; + convert::Property id; + convert::Property type; + static constexpr convert::PolicyFields kPolicyFields = + convert::PolicyFields::ConvertAll; }; + +static_assert(utils::convert::IsConvertAll); } // namespace timetable_vsu_backend::models diff --git a/src/models/user_credentials/fwd.hpp b/src/models/user_credentials/fwd.hpp new file mode 100644 index 00000000..8cec434c --- /dev/null +++ b/src/models/user_credentials/fwd.hpp @@ -0,0 +1,4 @@ +#pragma once +namespace timetable_vsu_backend::models { +struct UserCredentials; +} diff --git a/src/models/user_credentials/postgre.hpp b/src/models/user_credentials/postgre.hpp new file mode 100644 index 00000000..21a64f0b --- /dev/null +++ b/src/models/user_credentials/postgre.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +#include "models/user_credentials/type.hpp" +#include "utils/convert/drop_properties_ref.hpp" + +namespace timetable_vsu_backend::models { +using TupleUserCredentialsRaw = + timetable_vsu_backend::utils::convert::drop_properties_to_ref_const_t< + UserCredentials>; +// using TupleUserCredentials = +// userver::utils::StrongTypedef; +using TupleUserCredentials = TupleUserCredentialsRaw; +} // namespace timetable_vsu_backend::models + +namespace userver::storages::postgres::io { + +template <> +struct CppToUserPg { + static constexpr DBTypeName postgres_name = + "vsu_timetable.user_credentials"; +}; +} // namespace userver::storages::postgres::io diff --git a/src/models/user_credentials/type.hpp b/src/models/user_credentials/type.hpp new file mode 100644 index 00000000..16d644d9 --- /dev/null +++ b/src/models/user_credentials/type.hpp @@ -0,0 +1,15 @@ +#pragma once +#include + +#include "utils/convert/base.hpp" +namespace timetable_vsu_backend::models { +using namespace utils::convert; +struct UserCredentials { + Property login; + Property password; + static constexpr TypeOfBody kTypeOfBody = + TypeOfBody::Json; //открываем возможность использовать структуру, как + //запрос + static constexpr PolicyFields kPolicyFields = PolicyFields::ConvertAll; +}; +} // namespace timetable_vsu_backend::models diff --git a/src/models/user_type/postgre.hpp b/src/models/user_type/postgre.hpp index 991edd99..36d1a549 100644 --- a/src/models/user_type/postgre.hpp +++ b/src/models/user_type/postgre.hpp @@ -10,13 +10,12 @@ using timetable_vsu_backend::models::UserType; template <> struct CppToUserPg : EnumMappingBase { static constexpr userver::storages::postgres::DBTypeName postgres_name = - "vsu_timetable.usertype"; + "vsu_timetable.user_type"; static constexpr userver::utils::TrivialBiMap enumerators = [](auto selector) { return selector() .Case("user", UserType::kUser) .Case("admin", UserType::kAdmin) - .Case("root", UserType::kRoot) .Case("teacher", UserType::kTeacher); }; }; diff --git a/src/utils/convert/detail/drop_properties/const_dropper_to_ref.hpp b/src/utils/convert/detail/drop_properties/const_dropper_to_ref.hpp index ce57dd69..87ec7eaa 100644 --- a/src/utils/convert/detail/drop_properties/const_dropper_to_ref.hpp +++ b/src/utils/convert/detail/drop_properties/const_dropper_to_ref.hpp @@ -15,6 +15,7 @@ #include "utils/convert/additional_properties.hpp" #include "utils/type_holder.hpp" namespace timetable_vsu_backend::utils::convert::detail::drop_properties { + template struct ConstDropperToRef final { static auto Do(const T& t) { @@ -117,6 +118,7 @@ struct ConstDropperToRef final { using RawType = typename Member::value_type; return ConstDropperToRef::Do(member()); } + template static decltype(auto) HelpHandleMember(Tuple& tuple) { return HandleMember(std::get(tuple)); diff --git a/src/views/get-timetable/view.cpp b/src/views/get-timetable/view.cpp index 38d5cd78..f24840b8 100644 --- a/src/views/get-timetable/view.cpp +++ b/src/views/get-timetable/view.cpp @@ -35,14 +35,14 @@ namespace timetable_vsu_backend::views::get_timetable { namespace { -using components::controllers::postgres::LessonDetailsController; +namespace pg = components::controllers::postgres; class Handler final : public http::HandlerParsed { public: static constexpr std::string_view kName = "handler-get-timetable"; Handler(const userver::components::ComponentConfig& config, const userver::components::ComponentContext& context) : HandlerParsed(config, context), - lesson_controller(context.FindComponent()) { + lesson_controller(context.FindComponent()) { } static void ValidateBeginEnd(Request& request) { if (request.filter() and request.filter()->begin() and @@ -61,7 +61,7 @@ class Handler final : public http::HandlerParsed { } private: - const LessonDetailsController& lesson_controller; + const pg::lesson::Controller& lesson_controller; }; } // namespace diff --git a/src/views/login/Request.hpp b/src/views/login/Request.hpp index 321ddaa3..c5d268e5 100644 --- a/src/views/login/Request.hpp +++ b/src/views/login/Request.hpp @@ -1,20 +1,7 @@ #pragma once -#include -#include -#include -#include - -#include "utils/convert/base.hpp" +#include "models/user_credentials/type.hpp" #include "utils/convert/http_request_parse.hpp" #include "utils/convert/json_parse.hpp" - namespace timetable_vsu_backend::views::login { -using namespace utils::convert; -struct Request { - Property login; - Property password; - static constexpr TypeOfBody kTypeOfBody = TypeOfBody::Json; - static constexpr PolicyFields kPolicyFields = PolicyFields::ConvertAll; -}; -static_assert(HasTypeOfBody); +using Request = models::UserCredentials; } // namespace timetable_vsu_backend::views::login diff --git a/src/views/login/Responses.hpp b/src/views/login/Responses.hpp index c0e14613..e91a0e1a 100644 --- a/src/views/login/Responses.hpp +++ b/src/views/login/Responses.hpp @@ -3,6 +3,7 @@ #include #include "http/ErrorV1.hpp" +#include "models/user/type.hpp" #include "models/user_type/serialize.hpp" #include "models/user_type/type.hpp" #include "utils/convert/base.hpp" @@ -13,7 +14,7 @@ namespace timetable_vsu_backend::views::login { using namespace utils::convert; struct Response200 { Property id; - Property user_type; + Property user; static constexpr TypeOfBody kTypeOfBody = TypeOfBody::Json; static constexpr PolicyFields kPolicyFields = PolicyFields::ConvertAll; static constexpr userver::server::http::HttpStatus kStatusCode = diff --git a/src/views/login/view.cpp b/src/views/login/view.cpp index ff48931b..b85f8c85 100644 --- a/src/views/login/view.cpp +++ b/src/views/login/view.cpp @@ -13,13 +13,16 @@ #include "components/controllers/postgres/user/controller.hpp" #include "http/handler_parsed.hpp" #include "models/auth_token/serialize.hpp" -#include "models/user_type/type.hpp" +#include "models/user/serialize.hpp" +#include "models/user_type/serialize.hpp" namespace timetable_vsu_backend::views::login { +static_assert( + userver::formats::common::impl::kHasSerialize< + userver::formats::json::Value, timetable_vsu_backend::models::User>); namespace { -using components::controllers::postgres::TokenController; -using components::controllers::postgres::UserController; +namespace pg = components::controllers::postgres; class Handler final : public http::HandlerParsed { static Response401 PerformInvalidCredentials() { @@ -34,29 +37,33 @@ class Handler final : public http::HandlerParsed()), - token_controller(context.FindComponent()) { + user_controller(context.FindComponent()), + token_controller(context.FindComponent()) { } Response Handle(Request&& request) const override { - auto user = user_controller.GetByLogin(request.login()); - if (!user || user->password != request.password()) { + auto user = user_controller.GetByCredentials(request); + if (!user) { return PerformInvalidCredentials(); } auto id = token_controller.CreateNew( - user->id, userver::utils::datetime::Now() + std::chrono::hours(24)); + user->id(), + userver::utils::datetime::Now() + std::chrono::hours(24)); if (!id) { LOG_WARNING() << fmt::format( "Failed to create token for user, id: {}", - boost::uuids::to_string(user->id.GetUnderlying())); + boost::uuids::to_string(user->id())); return Response500{}; } - return Response200{{*id}, {models::UserType::kUser}}; + Response200 resp; + resp.id() = *id; + resp.user() = *user; + return resp; } private: - const UserController& user_controller; - const TokenController& token_controller; + const pg::user::Controller& user_controller; + const pg::token::Controller& token_controller; }; } // namespace diff --git a/src/views/register/Request.cpp b/src/views/register/Request.cpp deleted file mode 100644 index 7bec7a73..00000000 --- a/src/views/register/Request.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "Request.hpp" - -#include -#include -#include -#include - -#include "utils/json_type.hpp" - -namespace timetable_vsu_backend::views::register_ { -Request Parse(const userver::server::http::HttpRequest& value, - userver::formats::parse::To) { - auto body = userver::formats::json::FromString(value.RequestBody()); - auto login = body["login"].As(); - auto password = body["password"].As(); - return Request{std::move(login), std::move(password)}; -} -} // namespace timetable_vsu_backend::views::register_ diff --git a/src/views/register/Request.hpp b/src/views/register/Request.hpp index bc3f5bdf..a9731bda 100644 --- a/src/views/register/Request.hpp +++ b/src/views/register/Request.hpp @@ -1,16 +1,7 @@ #pragma once -#include -#include - -namespace userver::server::http { -class HttpRequest; -} - +#include "models/user_credentials/type.hpp" +#include "utils/convert/http_request_parse.hpp" +#include "utils/convert/json_parse.hpp" namespace timetable_vsu_backend::views::register_ { -struct Request { - std::string login; - std::string password; -}; -Request Parse(const userver::server::http::HttpRequest& value, - userver::formats::parse::To); +using Request = models::UserCredentials; } // namespace timetable_vsu_backend::views::register_ diff --git a/src/views/register/Responses.hpp b/src/views/register/Responses.hpp new file mode 100644 index 00000000..9522852e --- /dev/null +++ b/src/views/register/Responses.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +#include "http/ErrorV1.hpp" +#include "models/user/type.hpp" +#include "models/user_type/serialize.hpp" +#include "models/user_type/type.hpp" +#include "utils/convert/base.hpp" +#include "utils/convert/http_response_serialize.hpp" +#include "utils/convert/json_parse.hpp" + +namespace timetable_vsu_backend::views::register_ { +using namespace utils::convert; +struct Response200 { + Property id; + Property user; + static constexpr TypeOfBody kTypeOfBody = TypeOfBody::Json; + static constexpr PolicyFields kPolicyFields = PolicyFields::ConvertAll; + static constexpr userver::server::http::HttpStatus kStatusCode = + userver::server::http::HttpStatus::kOk; +}; +using Response400 = + http::ErrorV1; +struct Response500 { + static constexpr TypeOfBody kTypeOfBody = TypeOfBody::Empty; + static constexpr PolicyFields kPolicyFields = PolicyFields::ConvertAll; + static constexpr userver::server::http::HttpStatus kStatusCode = + userver::server::http::HttpStatus::kInternalServerError; +}; +} // namespace timetable_vsu_backend::views::register_ diff --git a/src/views/register/view.cpp b/src/views/register/view.cpp index 23032346..9e0ac285 100644 --- a/src/views/register/view.cpp +++ b/src/views/register/view.cpp @@ -17,53 +17,53 @@ #include "Request.hpp" #include "components/controllers/postgres/token/controller.hpp" #include "components/controllers/postgres/user/controller.hpp" -#include "http/legacy_handler_parsed.hpp" +#include "http/handler_parsed.hpp" #include "models/auth_token/serialize.hpp" +#include "models/user/serialize.hpp" +#include "views/register/Responses.hpp" namespace timetable_vsu_backend::views::register_ { namespace { -using components::controllers::postgres::TokenController; -using components::controllers::postgres::UserController; +namespace pg = components::controllers::postgres; using Response = models::AuthToken; -class Handler final : public http::LegacyHandlerParsed { +class Handler final : public http::HandlerParsed { public: static constexpr std::string_view kName = "handler-register"; - using http::LegacyHandlerParsed::LegacyHandlerParsed; + using http::HandlerParsed::HandlerParsed; Handler(const userver::components::ComponentConfig& config, const userver::components::ComponentContext& context) - : LegacyHandlerParsed(config, context), - user_controller(context.FindComponent()), - token_controller(context.FindComponent()) { + : HandlerParsed(config, context), + user_controller(context.FindComponent()), + token_controller(context.FindComponent()) { } - Response Handle( - Request&& request, - userver::server::http::HttpResponse& http_response) const override { - models::User user{models::User::Id{}, request.login, request.password}; - auto user_id = user_controller.TryToAdd(user); + Response Handle(Request&& request) const override { + auto user_id = user_controller.TryToAdd(request); if (!user_id) { LOG_DEBUG() << fmt::format("Cannot create user, login: {}", - user.login); - http_response.SetStatus(HttpStatus::kBadRequest); - return {}; + request.login()); + return Response400{}; } - user.id = models::User::Id{std::move(*user_id)}; auto id = token_controller.CreateNew( - user.id, userver::utils::datetime::Now() + std::chrono::hours(24)); + *user_id, userver::utils::datetime::Now() + std::chrono::hours(24)); if (!id) { LOG_WARNING() << fmt::format( - "Failed to create token for user, id: {}", - boost::uuids::to_string(user.id.GetUnderlying())); - http_response.SetStatus(HttpStatus::kInternalServerError); - return {}; + "Failed to create token for user, id: {}", *user_id); + return Response500{}; } - return {*id}; + Response200 resp; + resp.id() = *id; + resp.user() = + models::User{.id = {*user_id}, .type = {models::UserType::kUser}}; + return resp; } private: - const UserController& user_controller; - const TokenController& token_controller; + const pg::user::Controller& user_controller; + const pg::token::Controller& token_controller; }; } // namespace diff --git a/tests/login/conftest.py b/tests/login/conftest.py new file mode 100644 index 00000000..edd5bc71 --- /dev/null +++ b/tests/login/conftest.py @@ -0,0 +1,21 @@ +import pytest +K_ADD_USER = """ +insert into vsu_timetable."user"(id, login, password) values ('{}', '{}', '{}') +""" + + +@pytest.fixture(name='vsu_timetable_db') +def mock_vsu_timetable_db(pgsql): + class Context: + def __init__(self, pgsql): + pgsql = pgsql + + def add_user(self, user_id, login, password): + assert isinstance(user_id, str) + assert isinstance(login, str) + assert isinstance(password, str) + db = pgsql['db_1'] + cursor = db.cursor() + formated_query = K_ADD_USER.format(user_id, login, password) + cursor.execute(formated_query) + return Context(pgsql) diff --git a/tests/login/test_login_basic.py b/tests/login/test_login_basic.py index 2153c446..cabbb98c 100644 --- a/tests/login/test_login_basic.py +++ b/tests/login/test_login_basic.py @@ -4,14 +4,37 @@ @pytest.mark.pgsql('db_1', files=['initial_data_auth.sql']) -async def test_login_successful(service_client): - credentials = {'login': 'some_nickname', 'password': 'some_password'} +@pytest.mark.parametrize( + 'type, user_id', + [('user', '111111c7-9654-4814-b36b-7d39c1ddded2'), + ('teacher', '222111c7-9654-4814-b36b-7d39c1ddded2'), + ('admin', '333111c7-9654-4814-b36b-7d39c1ddded2')], +) +async def test_login_successful(service_client, type, user_id): + credentials = {'login': 'some_' + type, 'password': type + '_password'} response = await service_client.post('/login', json=credentials) assert response.status_code == 200 assert 'token' in response.json() - assert 'user_type' in response.json() - assert response.json()['user_type'] == 'user' + assert isinstance(response.json()['user'], dict) + assert response.json()['user']['id'] == user_id + assert response.json()['user']['type'] == type + + +@pytest.mark.pgsql('db_1', files=['initial_data_auth.sql']) +async def test_login_root_successful(service_client, vsu_timetable_db): + vsu_timetable_db.add_user( + 'dddddddd-dddd-dddd-dddd-dddddddddddd', + 'root', + 'secret-password' + ) + credentials = {'login': 'root', 'password': 'secret-password'} + response = await service_client.post('/login', json=credentials) + assert response.status_code == 200 + assert 'token' in response.json() + assert isinstance(response.json()['user'], dict) + assert response.json()['user']['id'] + assert response.json()['user']['type'] == 'root' @pytest.mark.pgsql('db_1', files=['initial_data_auth.sql']) diff --git a/united_api.yaml b/united_api.yaml index 515fb5d9..4b2bd986 100644 --- a/united_api.yaml +++ b/united_api.yaml @@ -156,6 +156,16 @@ components: - admin - root type: string + TypesUserV1: + properties: + id: + $ref: '#/components/schemas/TypesId' + type: + $ref: '#/components/schemas/TypesUserTypeV1' + required: + - id + - type + type: object ViewsAdminGroupAddRequest: properties: group_course: @@ -227,7 +237,7 @@ components: type: string type: array end: - ref: ../../types/Timestamp.yaml + $ref: '#/components/schemas/TypesTimestamp' faculty_ids: items: $ref: '#/components/schemas/TypesId' @@ -250,7 +260,7 @@ components: type: array group_types: items: - type: ../../types/EducationType.yaml + $ref: '#/components/schemas/TypesEducationType' type: array lesson_ids: items: @@ -332,8 +342,8 @@ components: properties: token: $ref: '#/components/schemas/TypesAuthTokenV1' - user_type: - $ref: '#/components/schemas/TypesUserTypeV1' + user: + $ref: '#/components/schemas/TypesUserV1' ViewsRegisterRequestV1: properties: user_credential: @@ -787,9 +797,6 @@ paths: description: Логин уже занят или пароль не удолетворяет требованиям tags: - users -servers: -- description: SwaggerHub API Auto Mocking - url: https://virtserver.swaggerhub.com/SABUDILOVSKIY_1/CourseWork/1.0.0 tags: - description: Доступно администраторам сервиса name: admins