get_answer_key_details()#

Belirtilen sınava ait cevap anahtarı detaylarını yetki kontrolü ile getiren güvenli fonksiyon.


Fonksiyon İmzası#

CREATE OR REPLACE FUNCTION get_answer_key_details(p_exam_id INTEGER)
RETURNS JSON
SECURITY DEFINER
SET search_path TO 'public'

Amaç#

Bir sınava ait cevap anahtarını güvenli bir şekilde getirir. Öğretmenler sadece kendi sınıflarının sınavlarını, öğrenciler ise kayıtlı oldukları sınıfların sınavlarını görebilir.


Parametreler#

ParametreTipAçıklama
p_exam_idINTEGERCevap anahtarı istenilen sınavın ID’si

Dönüş Değeri#

Type: JSON

Başarılı Yanıt:

{
  "exam_id": 42,
  "exam_title": "Matematik Vize Sınavı",
  "class_id": 15,
  "status": "active",
  "system_status": "completed",
  "answer_key": {
    "1": "A",
    "2": "C",
    "3": "B"
  },
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-15T14:20:00Z"
}

Hata Kodları#

SQLSTATEMesajAçıklama
USER_NOT_FOUNDOturum açmanız gerekiyorKullanıcı auth.uid() bulunamadı
EXAM_NOT_FOUNDSınav bulunamadıBelirtilen ID’de sınav yok
UNAUTHORIZEDBu sınavı görüntüleme yetkiniz yokYetki kontrolü başarısız
INVALID_ROLEGeçersiz kullanıcı rolüRole_id 1 veya 2 değil
NO_ANSWER_KEYBu sınav için henüz cevap anahtarı yüklenmemişAnswer_key NULL

SQL Kodu#

CREATE OR REPLACE FUNCTION public.get_answer_key_details(p_exam_id integer)
 RETURNS json
 LANGUAGE plpgsql
 SECURITY DEFINER
 SET search_path TO 'public'
AS $function$
DECLARE
  v_user_id int;
  v_user_role_id int;
  v_exam_record record;
  v_class_teacher_id int;
  v_is_class_member boolean := false;
  v_result json;
BEGIN
  SELECT id, role_id
  INTO v_user_id, v_user_role_id
  FROM users
  WHERE auth_user_id = auth.uid();

  IF v_user_id IS NULL THEN
    RAISE EXCEPTION 'Oturum açmanız gerekiyor.'
      USING HINT = 'USER_NOT_FOUND';
  END IF;

  SELECT e.*, c.teacher_id
  INTO v_exam_record
  FROM exams e
  INNER JOIN classes c ON e.class_id = c.id
  WHERE e.id = p_exam_id;

  IF NOT FOUND THEN
    RAISE EXCEPTION 'Sınav bulunamadı.'
      USING HINT = 'EXAM_NOT_FOUND';
  END IF;

  v_class_teacher_id := v_exam_record.teacher_id;

  IF v_user_role_id = 2 THEN
    IF v_class_teacher_id != v_user_id THEN
      RAISE EXCEPTION 'Bu sınavı görüntüleme yetkiniz yok.'
        USING HINT = 'UNAUTHORIZED';
    END IF;
  ELSIF v_user_role_id = 1 THEN
    SELECT EXISTS(
      SELECT 1 FROM class_members
      WHERE class_id = v_exam_record.class_id
        AND student_id = v_user_id
        AND deleted_at IS NULL
    ) INTO v_is_class_member;

    IF NOT v_is_class_member THEN
      RAISE EXCEPTION 'Bu sınavı görüntüleme yetkiniz yok.'
        USING HINT = 'UNAUTHORIZED';
    END IF;
  ELSE
    RAISE EXCEPTION 'Geçersiz kullanıcı rolü.'
      USING HINT = 'INVALID_ROLE';
  END IF;

  IF v_exam_record.answer_key IS NULL THEN
    RAISE EXCEPTION 'Bu sınav için henüz cevap anahtarı yüklenmemiş.'
      USING HINT = 'NO_ANSWER_KEY';
  END IF;

  v_result := json_build_object(
    'exam_id', v_exam_record.id,
    'exam_title', v_exam_record.title,
    'class_id', v_exam_record.class_id,
    'status', v_exam_record.status,
    'system_status', v_exam_record.system_status,
    'answer_key', v_exam_record.answer_key,
    'created_at', v_exam_record.created_at,
    'updated_at', v_exam_record.updated_at
  );

  RETURN v_result;

EXCEPTION
  WHEN OTHERS THEN
    RAISE EXCEPTION '%', SQLERRM
      USING HINT = SQLSTATE;
END;
$function$

Kullanım Örneği#

Projede Kullanım#

Dosya: lib/common/services/api/exam_api.dart
Satır: ~217
Açıklama: Exam API servisinde sınav cevap anahtarını çekmek için kullanılır.

final response = await _supabase.rpc(
  'get_answer_key_details',
  params: {'p_exam_id': examId}
);

SQL Örneği#

-- Öğretmen olarak kendi sınavının cevap anahtarını getir
SELECT get_answer_key_details(42);

-- Öğrenci olarak kayıtlı olduğu sınıfın sınavını getir
SELECT get_answer_key_details(42);

-- Hata durumu: Yetkisiz erişim
SELECT get_answer_key_details(999);
-- ERROR: Bu sınavı görüntüleme yetkiniz yok

Güvenlik Özellikleri#

  • SECURITY DEFINER: Fonksiyon owner yetkisiyle çalışır
  • SET search_path: SQL injection koruması
  • Row Level Security: Kullanıcı bazlı erişim kontrolü
  • Role-based Access: Öğretmen/öğrenci ayrımı
  • Soft Delete Check: deleted_at kontrolü

İlgili Tablolar#

  • public.exams
  • public.classes
  • public.users
  • public.class_members
  • auth.users

Notlar#

Dikkat: Bu fonksiyon sadece cevap anahtarını döndürür. Öğrencilerin cevaplarıyla karşılaştırma yapmaz.

Güvenlik: SECURITY DEFINER kullanıldığı için, fonksiyon içindeki tüm kontroller kritik öneme sahiptir.