| | 294 | |
| | 295 | Тригерот `trg_anti_spam_chat`, кој ја извршува тригер функцијата `fn_enforce_chat_security`, дејствува пред секое внесување на нова порака во табелата со цел да обезбеди безбедност и заштита од несакани содржини. Првиот чекор на овој механизам е да провери дали корисникот кој се обидува да ја испрати пораката е всушност заведен како студент или ментор во конкретниот разговор, со што се спречува неовластен пристап и упад од надворешни лица. Вториот чекор претставува заштита против спам која пресметува колку пораки испратил истиот корисник во тој разговор во последните 5 минути. Доколку овој број го надмине лимитот од 10 пораки, тригерот автоматски го блокира внесувањето и исфрла безбедносен исклучок. |
| | 296 | |
| | 297 | {{{ |
| | 298 | CREATE OR REPLACE FUNCTION fn_enforce_chat_security() |
| | 299 | RETURNS TRIGGER AS $$ |
| | 300 | DECLARE |
| | 301 | v_chat_owner_student BIGINT; |
| | 302 | v_chat_owner_mentor BIGINT; |
| | 303 | v_recent_messages INT; |
| | 304 | BEGIN |
| | 305 | -- 1. Валидација на пристап (Дали е овластен за овој чет?) |
| | 306 | SELECT StudentID, MentorID INTO v_chat_owner_student, v_chat_owner_mentor |
| | 307 | FROM Chat WHERE ID = NEW.ChatID; |
| | 308 | |
| | 309 | IF NEW.UserID != v_chat_owner_student AND NEW.UserID != v_chat_owner_mentor THEN |
| | 310 | RAISE EXCEPTION 'ГРЕШКА: Безбедносен прекршок! Корисникот % не е учесник во чет %.', NEW.UserID, NEW.ChatID; |
| | 311 | END IF; |
| | 312 | |
| | 313 | -- 2. Anti-Spam Заштита (Макс 10 пораки во 5 минути) |
| | 314 | SELECT COUNT(*) INTO v_recent_messages FROM Message |
| | 315 | WHERE UserID = NEW.UserID AND ChatID = NEW.ChatID AND Timestamp > (NOW() - INTERVAL '5 minutes'); |
| | 316 | |
| | 317 | IF v_recent_messages >= 10 THEN |
| | 318 | RAISE EXCEPTION 'ПРЕДУПРЕДУВАЊЕ: Го надминавте лимитот за праќање пораки. Обидете се подоцна!'; |
| | 319 | END IF; |
| | 320 | |
| | 321 | RETURN NEW; |
| | 322 | END; |
| | 323 | $$ LANGUAGE plpgsql; |
| | 324 | |
| | 325 | DROP TRIGGER IF EXISTS trg_anti_spam_chat ON Message; |
| | 326 | CREATE TRIGGER trg_anti_spam_chat |
| | 327 | BEFORE INSERT ON Message |
| | 328 | FOR EACH ROW EXECUTE FUNCTION fn_enforce_chat_security(); |
| | 329 | |
| | 330 | |
| | 331 | -- ТЕСТИРАЊЕ |
| | 332 | INSERT INTO Message (Content, isRead, Timestamp, ChatID, UserID) VALUES ('Тест спам порака', false, NOW(), 5, 1); |
| | 333 | |
| | 334 | --INSERT INTO Chat (Title, Topic, Status, "Date", StudentID, MentorID) VALUES ('Тест разговор за спам', 'Комуникација', 0, NOW(), 500, 300); |
| | 335 | |
| | 336 | }}} |
| | 337 | |
| | 338 | ---- |
| | 339 | |
| | 340 | Тригерот `trg_strict_task_timeline`, придружен со функцијата `fn_guard_task_timeline`, е задолжен за одржување на временскиот интегритет на задачите и се активира пред секое внесување или ажурирање на податоците во табелата за задачи. Тој забранува поставување на краен рок кој е еднаков или пред почетниот датум на задачата. Дополнително, при креирање на сосема нова задача, тригерот не дозволува нејзиниот краен рок да биде поставен во минатото во однос на тековниот ден. Исто така, доколку се прави обид за ажурирање на задача која веќе е завршена, тригерот ја одбива промената на крајниот рок со цел да ги заклучи минатите временски рамки. |
| | 341 | |
| | 342 | {{{ |
| | 343 | CREATE OR REPLACE FUNCTION fn_guard_task_timeline() |
| | 344 | RETURNS TRIGGER AS $$ |
| | 345 | BEGIN |
| | 346 | -- Дали EndDate е пред StartDate? |
| | 347 | IF NEW.EndDate <= NEW.StartDate THEN |
| | 348 | RAISE EXCEPTION 'ГРЕШКА: Крајниот рок мора да биде по почетниот датум!'; |
| | 349 | END IF; |
| | 350 | |
| | 351 | -- Ако е ВНЕСУВАЊЕ, не смее крајниот рок да е во минатото |
| | 352 | IF TG_OP = 'INSERT' AND NEW.EndDate < CURRENT_DATE THEN |
| | 353 | RAISE EXCEPTION 'ГРЕШКА: Не можете да креирате нова задача со краен рок во минатото!'; |
| | 354 | END IF; |
| | 355 | |
| | 356 | -- Ако е АЖУРИРАЊЕ и задачата е веќе завршена |
| | 357 | IF TG_OP = 'UPDATE' AND OLD.Status = 1 AND NEW.EndDate != OLD.EndDate THEN |
| | 358 | RAISE EXCEPTION 'ОДБИЕНО: Задачата е веќе завршена. Временските рамки се заклучени!'; |
| | 359 | END IF; |
| | 360 | |
| | 361 | RETURN NEW; |
| | 362 | END; |
| | 363 | $$ LANGUAGE plpgsql; |
| | 364 | |
| | 365 | DROP TRIGGER IF EXISTS trg_strict_task_timeline ON Task; |
| | 366 | CREATE TRIGGER trg_strict_task_timeline |
| | 367 | BEFORE INSERT OR UPDATE ON Task |
| | 368 | FOR EACH ROW EXECUTE FUNCTION fn_guard_task_timeline(); |
| | 369 | |
| | 370 | |
| | 371 | -- ТЕСТИРАЊЕ |
| | 372 | INSERT INTO Task (MentorshipID, description , StartDate, EndDate, Status) VALUES (1, 'Невалидна Задача', NOW(), NOW() - INTERVAL '7 days', 0); |
| | 373 | |
| | 374 | }}} |
| | 375 | |
| | 376 | ---- |
| | 377 | |
| | 378 | Тригерот `trg_topic_state_machine` ја извршува функцијата `fn_topic_state_guard` пред секое ажурирање на податоците во табелата за предложени теми за менторство со цел да го контролира и заштити животниот циклус на темите. При обид за менување на матичниот предмет на кој му припаѓа веќе креирана тема, овој механизам ја прекинува операцијата и исфрла грешка за да спречи конфузија во наставната програма. Дополнително, тригерот спречува рачно менување на статусот на темата од зафатена во слободна доколку таа тема сè уште е поврзана со некое активно менторство кое содржи незавршени задачи, со што се гарантира дека темите нема предвремено и нелогично да се ослободат во системот. |
| | 379 | |
| | 380 | {{{ |
| | 381 | |
| | 382 | CREATE OR REPLACE FUNCTION fn_topic_state_guard() |
| | 383 | RETURNS TRIGGER AS $$ |
| | 384 | DECLARE |
| | 385 | v_active_mentorships INT; |
| | 386 | BEGIN |
| | 387 | -- Забрането менување на предмет откако темата е креирана |
| | 388 | IF TG_OP = 'UPDATE' AND OLD.SubjectID != NEW.SubjectID THEN |
| | 389 | RAISE EXCEPTION 'ПРЕДУПРЕДУВАЊЕ: Откако темата е предложена, забрането е менување на нејзиниот предмет!'; |
| | 390 | END IF; |
| | 391 | |
| | 392 | -- Ако некој рачно пробува да ја ослободи темата (isAvailable -> true) |
| | 393 | IF TG_OP = 'UPDATE' AND OLD.isAvailable = false AND NEW.isAvailable = true THEN |
| | 394 | SELECT COUNT(*) INTO v_active_mentorships FROM Mentorship m |
| | 395 | JOIN Task t ON m.ID = t.MentorshipID |
| | 396 | WHERE m.TopicSuggestionID = NEW.ID AND t.Status != 1; |
| | 397 | |
| | 398 | IF v_active_mentorships > 0 THEN |
| | 399 | RAISE EXCEPTION 'ПРЕДУПРЕДУВАЊЕ: Темата не може да се ослободи бидејќи сè уште е поврзана со активно менторство!'; |
| | 400 | END IF; |
| | 401 | END IF; |
| | 402 | |
| | 403 | RETURN NEW; |
| | 404 | END; |
| | 405 | $$ LANGUAGE plpgsql; |
| | 406 | |
| | 407 | DROP TRIGGER IF EXISTS trg_topic_state_machine ON TopicSuggestion; |
| | 408 | CREATE TRIGGER trg_topic_state_machine |
| | 409 | BEFORE UPDATE ON TopicSuggestion |
| | 410 | FOR EACH ROW EXECUTE FUNCTION fn_topic_state_guard(); |
| | 411 | |
| | 412 | -- ТЕСТИРАЊЕ |
| | 413 | UPDATE TopicSuggestion SET SubjectID = 99999 WHERE ID = (SELECT ID FROM TopicSuggestion LIMIT 1); |
| | 414 | UPDATE TopicSuggestion SET isAvailable = true WHERE ID = (SELECT TopicSuggestionID FROM Mentorship LIMIT 1); |
| | 415 | --UPDATE TopicSuggestion SET isAvailable = true WHERE ID = (SELECT m.TopicSuggestionID FROM Mentorship m JOIN Task t ON m.ID = t.MentorshipID WHERE t.Status != 1 LIMIT 1); |
| | 416 | |
| | 417 | }}} |