diff --git a/.github/workflows/example.yml b/.github/workflows/example.yml index 9fceff9..960d714 100644 --- a/.github/workflows/example.yml +++ b/.github/workflows/example.yml @@ -7,6 +7,7 @@ on: env: DATABASE_URL: ${{secrets.DATABASE_URL}} + COOKIESTORE: ${{secrets.COOKIESTORE}} # FIREBASE_ADMIN_PASSPHRASE: ${{secrets.FIREBASE_ADMIN_PASSPHRASE}} jobs: diff --git a/adminHandler.go b/adminHandler.go index 49e6ee0..05199a8 100644 --- a/adminHandler.go +++ b/adminHandler.go @@ -43,13 +43,7 @@ func AdminHandler(router *mux.Router) { vars := mux.Vars(req) googleID := vars["googleID"] - currentUser, err := GetCurrentUser(req) - if err != nil { - http.Error(res, err.Error(), http.StatusInternalServerError) - return - } - - student, err := readStudent(currentUser, googleID) + student, err := readStudent(googleID, req) if err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return @@ -67,15 +61,9 @@ func AdminHandler(router *mux.Router) { vars := mux.Vars(req) googleID := vars["googleID"] - currentUser, err := GetCurrentUser(req) - if err != nil { - http.Error(res, err.Error(), http.StatusInternalServerError) - return - } - switch req.Method { case http.MethodGet: - student, err := readStudent(currentUser, googleID) + student, err := readStudent(googleID, req) if err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return @@ -88,7 +76,7 @@ func AdminHandler(router *mux.Router) { http.Error(res, err.Error(), http.StatusBadRequest) return } - if err := updateStudent(currentUser, googleID, updates); err != nil { + if err := updateStudent(googleID, updates, req); err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return } @@ -120,13 +108,13 @@ func AdminHandler(router *mux.Router) { vars := mux.Vars(req) googleID := vars["googleID"] - currentUser, err := GetCurrentUser(req) - if err != nil { - http.Error(res, "Unauthorized", http.StatusUnauthorized) - return - } + // currentUser, err := GetCurrentUser(req) + // if err != nil { + // http.Error(res, "Unauthorized", http.StatusUnauthorized) + // return + // } - parent, err := readParent(currentUser, googleID) + parent, err := readParent(googleID, req) if err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return @@ -144,15 +132,15 @@ func AdminHandler(router *mux.Router) { vars := mux.Vars(req) googleID := vars["googleID"] - currentUser, err := GetCurrentUser(req) - if err != nil { - http.Error(res, "Unauthorized", http.StatusUnauthorized) - return - } + // currentUser, err := GetCurrentUser(req) + // if err != nil { + // http.Error(res, "Unauthorized", http.StatusUnauthorized) + // return + // } switch req.Method { case http.MethodGet: - parent, err := readParent(currentUser, googleID) + parent, err := readParent(googleID, req) if err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return @@ -165,7 +153,7 @@ func AdminHandler(router *mux.Router) { http.Error(res, err.Error(), http.StatusBadRequest) return } - if err := updateParent(currentUser, googleID, updates); err != nil { + if err := updateParent(googleID, updates, req); err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return } @@ -197,13 +185,13 @@ func AdminHandler(router *mux.Router) { vars := mux.Vars(req) googleID := vars["googleID"] - currentUser, err := GetCurrentUser(req) - if err != nil { - http.Error(res, "Unauthorized", http.StatusUnauthorized) - return - } + // currentUser, err := GetCurrentUser(req) + // if err != nil { + // http.Error(res, "Unauthorized", http.StatusUnauthorized) + // return + // } - instructor, err := readInstructor(currentUser, googleID) + instructor, err := readInstructor(googleID, req) if err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return @@ -221,15 +209,15 @@ func AdminHandler(router *mux.Router) { vars := mux.Vars(req) googleID := vars["googleID"] - currentUser, err := GetCurrentUser(req) - if err != nil { - http.Error(res, "Unauthorized", http.StatusUnauthorized) - return - } + // currentUser, err := GetCurrentUser(req) + // if err != nil { + // http.Error(res, "Unauthorized", http.StatusUnauthorized) + // return + // } switch req.Method { case http.MethodGet: - instructor, err := readInstructor(currentUser, googleID) + instructor, err := readInstructor(googleID, req) if err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return @@ -242,7 +230,7 @@ func AdminHandler(router *mux.Router) { http.Error(res, err.Error(), http.StatusBadRequest) return } - if err := updateInstructor(currentUser, googleID, updates); err != nil { + if err := updateInstructor(googleID, updates, req); err != nil { http.Error(res, err.Error(), http.StatusInternalServerError) return } diff --git a/database.go b/database.go index 5d71f0e..52c62a3 100644 --- a/database.go +++ b/database.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "net/http" + "os" "strings" firebase "firebase.google.com/go" @@ -14,7 +15,23 @@ import ( ) var firebaseClient *db.Client -var store = sessions.NewCookieStore([]byte("1L6x-SPtG8-EqJUkR7htTJx-5K4rt-ZTKeh-rxPw-AM=")) + +func SessionCookie() (string, error) { + // sessionCookieStore := goDotEnvVariable("SESSION_COOKIE_STORE") + // if sessionCookieStore == "" { + // return sessionCookieStore, fmt.Errorf("SESSION_COOKIE_STORE is not set in the environment variables") + // } + + sessionCookieStore, found := os.LookupEnv("COOKIESTORE") + if !found { + log.Fatalf("COOKIESTORE is not set in the environment variables") + } + + return sessionCookieStore, nil +} + +var sessionCookieStore, err = SessionCookie() +var store = sessions.NewCookieStore([]byte(sessionCookieStore)) func initDB(app *firebase.App) error { // Initialize Firebase client @@ -70,9 +87,9 @@ func isSelf(user User, googleID string) bool { } // Check if instructor can access student (student's class' instructor = instructor name) -func canInstructorAccessStudent(currentUser User, student Student, classes []Class) bool { +func canInstructorAccessStudent(user User, student Student, classes []Class) bool { for _, class := range classes { - if class.Instructor == currentUser.GoogleID && class.ClassID == student.ClassID { + if class.Instructor == user.GoogleID && class.ClassID == student.ClassID { return true } } @@ -80,21 +97,21 @@ func canInstructorAccessStudent(currentUser User, student Student, classes []Cla } // Check if parent can access child (student's parent's id = parent id) -func canParentAccessChild(currentUser User, student Student) bool { +func canParentAccessChild(user User, student Student) bool { // Implement logic to check if parent can access the child - return currentUser.GoogleID == student.ParentID + return user.GoogleID == student.ParentID } // Check if student can access parent (student's parent's name = parent name) -func canChildAccessParent(currentUser User, parent Parent) bool { +func canChildAccessParent(user User, parent Parent) bool { // Implement logic to check if parent can access the child - return currentUser.GoogleID == parent.GoogleID + return user.GoogleID == parent.GoogleID } // Check if student is in the class -func isStudentInClass(currentUser User, students []Student, class Class) bool { +func isStudentInClass(user User, students []Student, class Class) bool { for _, student := range students { - if student.GoogleID == currentUser.GoogleID && student.ClassID == class.ClassID { + if student.GoogleID == user.GoogleID && student.ClassID == class.ClassID { return true } } @@ -102,9 +119,9 @@ func isStudentInClass(currentUser User, students []Student, class Class) bool { } // Check if the parent's child is in the class -func isParentChildInClass(currentUser User, students []Student, class Class) bool { +func isParentChildInClass(user User, students []Student, class Class) bool { for _, student := range students { - if student.ParentID == currentUser.GoogleID && student.ClassID == class.ClassID { + if student.ParentID == user.GoogleID && student.ClassID == class.ClassID { return true } } @@ -156,7 +173,11 @@ func getUserRole(email string) (User, string, error) { // CRUD operations with role checks // Student CRUD -func createStudent(currentUser User, student Student) error { +func createStudent(student Student, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not an admin, return error when attempting to create student if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can create students") @@ -168,14 +189,19 @@ func createStudent(currentUser User, student Student) error { return ref.Set(context.TODO(), student) } -func readStudent(currentUser User, studentGoogleID string) (Student, error) { +func readStudent(studentGoogleID string, req *http.Request) (Student, error) { ref := firebaseClient.NewRef("students/" + studentGoogleID) var student Student if err := ref.Get(context.TODO(), &student); err != nil { return Student{}, fmt.Errorf("error reading student: %v", err) } - classes, err := readAllClasses(currentUser) + currentUser, err := GetCurrentUser(req) + if err != nil { + return Student{}, fmt.Errorf("error getting current user") + } + + classes, err := readAllClasses(req) if err != nil { return Student{}, fmt.Errorf("error reading classes: %v", err) } @@ -222,22 +248,26 @@ func searchStudents(name, class string) ([]Student, error) { return filteredStudents, nil } -func updateStudent(currentUser User, studentGoogleID string, updates map[string]interface{}) error { - // Fetch the student information using the provided GoogleID - student, err := readStudent(currentUser, studentGoogleID) +func updateStudent(studentGoogleID string, updates map[string]interface{}, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } + // Fetch the student information + student, err := readStudent(studentGoogleID, req) if err != nil { return fmt.Errorf("error fetching student: %v", err) } // Fetch all classes - classes, err := readAllClasses(currentUser) + classes, err := readAllClasses(req) if err != nil { return fmt.Errorf("error reading classes: %v", err) } // If user is not admin, instructor, or parent, return error when attempting to update student if !isAdmin(currentUser) && //not admin - !(isSelf(currentUser, student.GoogleID) && isStudent(currentUser)) && //not student and reading self + !(isSelf(currentUser, studentGoogleID) && isStudent(currentUser)) && //not student and reading self !(currentUser.Role == "Instructor" && canInstructorAccessStudent(currentUser, student, classes)) && //instructor can access only their students' info !(currentUser.Role == "Parent" && canParentAccessChild(currentUser, student)) { // parent can access only their child's info { return fmt.Errorf("unauthorized access: you can only update your own details") @@ -250,7 +280,11 @@ func updateStudent(currentUser User, studentGoogleID string, updates map[string] return nil } -func deleteStudent(currentUser User, student Student) error { +func deleteStudent(student Student, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to delete student if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can delete students") @@ -263,7 +297,11 @@ func deleteStudent(currentUser User, student Student) error { } // Instructor CRUD -func createInstructor(currentUser User, instructor Instructor) error { +func createInstructor(instructor Instructor, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to create instructor if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can create instructors") @@ -275,13 +313,18 @@ func createInstructor(currentUser User, instructor Instructor) error { return ref.Set(context.TODO(), instructor) } -func readInstructor(currentUser User, instructorGoogleID string) (Instructor, error) { +func readInstructor(instructorGoogleID string, req *http.Request) (Instructor, error) { ref := firebaseClient.NewRef("instructors/" + instructorGoogleID) var instructor Instructor if err := ref.Get(context.TODO(), &instructor); err != nil { return Instructor{}, fmt.Errorf("error reading instructor: %v", err) } + currentUser, err := GetCurrentUser(req) + if err != nil { + return Instructor{}, fmt.Errorf("error getting current user: %v", err) + } + // If user is not admin or (self & instructor), return error when attempting to read instructor if !isAdmin(currentUser) && //not admin !(isSelf(currentUser, instructor.GoogleID) && isInstructor(currentUser)) { @@ -322,9 +365,13 @@ func searchInstructors(name string) ([]Instructor, error) { return filteredInstructors, nil } -func updateInstructor(currentUser User, instructorGoogleID string, updates map[string]interface{}) error { +func updateInstructor(instructorGoogleID string, updates map[string]interface{}, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // Fetch the instructor information using the provided GoogleID - instructor, err := readInstructor(currentUser, instructorGoogleID) + instructor, err := readInstructor(instructorGoogleID, req) if err != nil { return fmt.Errorf("error fetching instructor: %v", err) } @@ -341,7 +388,11 @@ func updateInstructor(currentUser User, instructorGoogleID string, updates map[s return ref.Update(context.TODO(), updates) } -func deleteInstructor(currentUser User, instructor Instructor) error { +func deleteInstructor(instructor Instructor, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to delete instructor if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can delete instructors") @@ -354,7 +405,11 @@ func deleteInstructor(currentUser User, instructor Instructor) error { } // Admin CRUD -func createAdmin(currentUser User, admin Admin) error { +func createAdmin(admin Admin, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to create admin if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can create admins") @@ -366,13 +421,18 @@ func createAdmin(currentUser User, admin Admin) error { return ref.Set(context.TODO(), admin) } -func readAdmin(currentUser User, adminGoogleID string) (Admin, error) { +func readAdmin(adminGoogleID string, req *http.Request) (Admin, error) { ref := firebaseClient.NewRef("admins/" + adminGoogleID) var admin Admin if err := ref.Get(context.TODO(), &admin); err != nil { return Admin{}, fmt.Errorf("error reading admin: %v", err) } + currentUser, err := GetCurrentUser(req) + if err != nil { + return Admin{}, fmt.Errorf("error getting current user: %v", err) + } + // If user is not admin, return error when attempting to read admin if !isAdmin(currentUser) { return Admin{}, fmt.Errorf("unauthorized access: you can only read your own details") @@ -380,9 +440,13 @@ func readAdmin(currentUser User, adminGoogleID string) (Admin, error) { return admin, nil } -func updateAdmin(currentUser User, adminGoogleID string, updates map[string]interface{}) error { +func updateAdmin(adminGoogleID string, updates map[string]interface{}, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // Fetch the admin information using the provided GoogleID - admin, err := readAdmin(currentUser, adminGoogleID) + admin, err := readAdmin(adminGoogleID, req) if err != nil { return fmt.Errorf("error fetching admin: %v", err) } @@ -398,7 +462,11 @@ func updateAdmin(currentUser User, adminGoogleID string, updates map[string]inte return ref.Update(context.TODO(), updates) } -func deleteAdmin(currentUser User, admin Admin) error { +func deleteAdmin(admin Admin, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to delete admin if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can delete admins") @@ -411,7 +479,11 @@ func deleteAdmin(currentUser User, admin Admin) error { } // Parent CRUD -func createParent(currentUser User, parent Parent) error { +func createParent(parent Parent, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to create parent if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can create parents") @@ -423,13 +495,18 @@ func createParent(currentUser User, parent Parent) error { return ref.Set(context.TODO(), parent) } -func readParent(currentUser User, parentGoogleID string) (Parent, error) { +func readParent(parentGoogleID string, req *http.Request) (Parent, error) { ref := firebaseClient.NewRef("parents/" + parentGoogleID) var parent Parent if err := ref.Get(context.TODO(), &parent); err != nil { return Parent{}, fmt.Errorf("error reading parent: %v", err) } + currentUser, err := GetCurrentUser(req) + if err != nil { + return Parent{}, fmt.Errorf("error getting current user: %v", err) + } + // If user is not admin or (self and parent), return error when attempting to update parent if !isAdmin(currentUser) && //not admin !(isSelf(currentUser, parent.GoogleID) && isParent(currentUser)) && //not parent and reading self @@ -470,9 +547,13 @@ func searchParents(name string) ([]Parent, error) { return filteredParents, nil } -func updateParent(currentUser User, parentGoogleID string, updates map[string]interface{}) error { +func updateParent(parentGoogleID string, updates map[string]interface{}, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // Fetch the parent information using the provided GoogleID - parent, err := readParent(currentUser, parentGoogleID) + parent, err := readParent(parentGoogleID, req) if err != nil { return fmt.Errorf("error fetching parent: %v", err) } @@ -488,7 +569,11 @@ func updateParent(currentUser User, parentGoogleID string, updates map[string]in return ref.Update(context.TODO(), updates) } -func deleteParent(currentUser User, parent Parent) error { +func deleteParent(parent Parent, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to delete parent if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can delete parents") @@ -501,7 +586,11 @@ func deleteParent(currentUser User, parent Parent) error { } // class CRUD -func createClass(currentUser User, class Class) error { +func createClass(class Class, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to create class if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can create classes") @@ -513,13 +602,18 @@ func createClass(currentUser User, class Class) error { return ref.Set(context.TODO(), class) } -func readClass(currentUser User, students []Student, classID string) (Class, error) { +func readClass(students []Student, classID string, req *http.Request) (Class, error) { ref := firebaseClient.NewRef("classes/" + classID) var class Class if err := ref.Get(context.TODO(), &class); err != nil { return Class{}, fmt.Errorf("error reading class: %v", err) } + currentUser, err := GetCurrentUser(req) + if err != nil { + return Class{}, fmt.Errorf("error getting current user: %v", err) + } + // If user is not admin or (self and class), return error when attempting to read class if !isAdmin(currentUser) && //not admin !isInstructor(currentUser) && //not instructor @@ -530,7 +624,7 @@ func readClass(currentUser User, students []Student, classID string) (Class, err return class, nil } -func readAllClasses(currentUser User) ([]Class, error) { +func readAllClasses(req *http.Request) ([]Class, error) { ref := firebaseClient.NewRef("classes") var classesMap map[string]Class @@ -539,6 +633,10 @@ func readAllClasses(currentUser User) ([]Class, error) { } var classes []Class + currentUser, err := GetCurrentUser(req) + if err != nil { + return []Class{}, fmt.Errorf("error getting current user: %v", err) + } for _, class := range classesMap { // If user is not authorized to read the class, skip it if !isAdmin(currentUser) && // not admin @@ -551,7 +649,11 @@ func readAllClasses(currentUser User) ([]Class, error) { return classes, nil } -func updateClass(currentUser User, class Class, updates map[string]interface{}) error { +func updateClass(class Class, updates map[string]interface{}, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin or (self and class), return error when attempting to update class if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: you can only update your own details") @@ -563,7 +665,11 @@ func updateClass(currentUser User, class Class, updates map[string]interface{}) return ref.Update(context.TODO(), updates) } -func deleteClass(currentUser User, class Class) error { +func deleteClass(class Class, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to delete class if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can delete classes") @@ -576,34 +682,49 @@ func deleteClass(currentUser User, class Class) error { } // Announcements CRUD -func createAnnouncement(currentUser User, announcement Announcement) error { +func createAnnouncement(announcement Announcement, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to create announcement if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can create announcements") } - ref := firebaseClient.NewRef("admins/" + announcement.AnnouncementID) + ref := firebaseClient.NewRef("announcements/" + announcement.AnnouncementID) if err := ref.Set(context.TODO(), announcement); err != nil { return fmt.Errorf("error creating admin: %v", err) } return ref.Set(context.TODO(), announcement) } -func readAnnouncement(currentUser User, announcement Announcement) (Announcement, error) { - // If user is not admin, return error when attempting to read admin +func readAnnouncement(announcementID string, req *http.Request) (Announcement, error) { + ref := firebaseClient.NewRef("announcements/" + announcementID) + var announcement Announcement + if err := ref.Get(context.TODO(), &announcement); err != nil { + return Announcement{}, fmt.Errorf("error reading admin: %v", err) + } + + currentUser, err := GetCurrentUser(req) + if err != nil { + return Announcement{}, fmt.Errorf("error getting current user: %v", err) + } + + // If user is not NK user, return error when attempting to read admin if !isAdmin(currentUser) && !isInstructor(currentUser) && !isParent(currentUser) && !isStudent(currentUser) { return Announcement{}, fmt.Errorf("unauthorized access: you are not allowed to read this announcement") } - ref := firebaseClient.NewRef("announcements/" + announcement.AnnouncementID) - if err := ref.Get(context.TODO(), &announcement); err != nil { - return Announcement{}, fmt.Errorf("error reading admin: %v", err) - } return announcement, nil } -func updateAnnouncement(currentUser User, announcement Announcement, updates map[string]interface{}) error { +func updateAnnouncement(announcement Announcement, updates map[string]interface{}, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to update announcement if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can update this announcement") @@ -615,7 +736,11 @@ func updateAnnouncement(currentUser User, announcement Announcement, updates map return ref.Update(context.TODO(), updates) } -func deleteAnnouncement(currentUser User, announcement Announcement) error { +func deleteAnnouncement(announcement Announcement, req *http.Request) error { + currentUser, err := GetCurrentUser(req) + if err != nil { + return fmt.Errorf("error getting current user: %v", err) + } // If user is not admin, return error when attempting to delete announcement if !isAdmin(currentUser) { return fmt.Errorf("unauthorized access: only admins can delete announcements") diff --git a/database_test.go b/database_test.go index 38944d6..f0c58db 100644 --- a/database_test.go +++ b/database_test.go @@ -1,77 +1,126 @@ package main import ( + "log" + "net/http" + "net/http/httptest" "reflect" "testing" + + "encoding/gob" + "encoding/json" ) -var currentUser = User{GoogleID: "admin-user", Role: "Admin"} +func init() { + // Register the User type with gob + gob.Register(User{}) + gob.Register(Announcement{}) +} + +var ( + // testStore = sessions.NewCookieStore([]byte("a-very-secret-key")) + + currentUser = User{ + GoogleID: "admin-user", + Name: "Admin User", + Email: "jiaweicodetest@gmail.com", + ContactNumber: "12345678", + Role: "Admin", + } + + // Sample data for testing + students = []Student{ + { + User: User{ + GoogleID: "test-student", + Name: "John Doe", + Email: "jeyvianangjieen@gmail.com", + ContactNumber: "91234567", + Role: "Student", + }, + Age: 12, + LessonCredits: 10.0, + ClassID: "TE-6-10", + ParentID: "test-parent", + }, + } -// Create a new student -var students = []Student{ - { + instructor = Instructor{ User: User{ - GoogleID: "test-student", + GoogleID: "test-instructor", Name: "John Doe", Email: "jeyvianang112462@gmail.com", ContactNumber: "91234567", Role: "Student", }, - Age: 12, - LessonCredits: 10.0, - ClassID: "te-6-10", - ParentID: "test-parent", - }, -} + BasePay: 15, + NumberOfStudents: 24, + } -// Create a new instructor -var instructor = Instructor{ - User: User{ - GoogleID: "test-instructor", - Name: "Awesomeness", - Email: "awesome_instructor@nk.com", - ContactNumber: "99999999", - Role: "Instructor", - }, - BasePay: 15, - NumberOfStudents: 24, -} + admin = Admin{ + User: User{ + GoogleID: "test-admin", + Name: "Awesomeness", + ContactNumber: "99999999", + Email: "awesome_admin@nk.com", + Role: "Admin", + }, + BasePay: 15, + Incentive: 24, + } -// Create a new admin -var admin = Admin{ - User: User{ - GoogleID: "test-admin", - Name: "Awesomeness", - ContactNumber: "99999999", - Email: "jeyvianangjieen@gmail.com", - Role: "Admin", - }, - BasePay: 15, - Incentive: 24, -} + parent = Parent{ + User: User{ + GoogleID: "test-parent", + Name: "Awesomeness", + ContactNumber: "99999999", + Email: "janedoe_parent@nk.com", + Role: "Parent", + }, + } -// Create a new parent -var parent = Parent{ - User: User{ - GoogleID: "test-parent", - Name: "Awesomeness", - ContactNumber: "99999999", - Email: "janedoe_parent@nk.com", - Role: "Parent", - }, -} + classes = []Class{ + { + ClassID: "te-6-10", + Name: "Test Class", + }, + } -// Create a dummy class for testing -var classes = []Class{ - { - ClassID: "te-6-10", - Name: "Test Class", - }, -} + announcement = Announcement{ + AnnouncementID: "test-announcement", + Subject: "Test Announcement", + Content: "This is a test announcement.", + } +) + +// func mockRequest() *http.Request { +// req, _ := http.NewRequest("GET", "/test", nil) +// session, _ := store.Get(req, "session-name") +// session.Values["user"] = currentUser +// err := session.Save(req, httptest.NewRecorder()) +// if err != nil { +// log.Println(err) +// } +// return req +// } + +func mockRequest() *http.Request { + req, _ := http.NewRequest("GET", "/test", nil) + recorder := httptest.NewRecorder() + session, _ := store.Get(req, "auth-session") -var announcement = Announcement{ - Subject: "Test Announcement", - Content: "This is a test announcement.", + userData, err := json.Marshal(currentUser) + if err != nil { + log.Println(err) + } + + session.Values["user"] = userData + err = session.Save(req, recorder) + if err != nil { + log.Println(err) + } + req.Header.Set("Cookie", recorder.Header().Get("Set-Cookie")) + return req } func TestInitializeFirebase(t *testing.T) { @@ -90,13 +139,17 @@ func TestInitializeFirebase(t *testing.T) { // Testing for student CRUD operations func TestCreateStudent(t *testing.T) { - err := createStudent(currentUser, students[0]) + err := initializeFirebase() + if err != nil { + t.Fatalf("Error initializing Firebase: %v", err) + } + err = createStudent(students[0], mockRequest()) if err != nil { t.Fatalf("Error creating student: %v", err) } - readStudent, err := readStudent(currentUser, students[0].GoogleID) + readStudent, err := readStudent(students[0].GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading student: %v", err) } @@ -107,7 +160,7 @@ func TestCreateStudent(t *testing.T) { } func TestReadStudent(t *testing.T) { - readStudent, err := readStudent(currentUser, students[0].GoogleID) + readStudent, err := readStudent(students[0].GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading student: %v", err) } @@ -120,29 +173,30 @@ func TestReadStudent(t *testing.T) { func TestUpdateStudent(t *testing.T) { // Update the student's email updates := map[string]interface{}{ - "name": "Updated Student", + "name": "John Doe", + "contact_number": "99999999", } - err := updateStudent(currentUser, students[0].GoogleID, updates) + err := updateStudent(students[0].GoogleID, updates, mockRequest()) if err != nil { t.Fatalf("Error updating student: %v", err) } // Read the updated student - readStudent, err := readStudent(currentUser, students[0].GoogleID) + readStudent, err := readStudent(students[0].GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading student after updating: %v", err) } // Assert that the updated student's email is correct - if readStudent.Name != updates["name"] { + if students[0].Name != updates["name"] { t.Errorf("Updated student's name is incorrect. Expected: %v, Got: %v", updates["name"], readStudent.Name) } } func TestDeleteStudent(t *testing.T) { // Delete the student - err := deleteStudent(currentUser, students[0]) + err := deleteStudent(students[0], mockRequest()) if err != nil { t.Fatalf("Error deleting student: %v", err) } @@ -156,13 +210,13 @@ func TestDeleteStudent(t *testing.T) { // Testing for instructor CRUD operations func TestCreateInstructor(t *testing.T) { - err := createInstructor(currentUser, instructor) + err := createInstructor(instructor, mockRequest()) if err != nil { t.Fatalf("Error creating instructor: %v", err) } // Read the created instructor - readInstructor, err := readInstructor(currentUser, instructor.GoogleID) + readInstructor, err := readInstructor(instructor.GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading instructor: %v", err) } @@ -175,7 +229,7 @@ func TestCreateInstructor(t *testing.T) { func TestReadInstructor(t *testing.T) { - instructor, err := readInstructor(currentUser, instructor.GoogleID) + instructor, err := readInstructor(instructor.GoogleID, mockRequest()) if err != nil { t.Fatalf("Failed to read instructor: %v", err) } @@ -191,13 +245,13 @@ func TestUpdateInstructor(t *testing.T) { "email": "amazing_instructor@nk.com", } - err := updateInstructor(currentUser, instructor.GoogleID, updates) + err := updateInstructor(instructor.GoogleID, updates, mockRequest()) if err != nil { t.Fatalf("Error updating instructor: %v", err) } // Read the updated instructor - readInstructor, err := readInstructor(currentUser, instructor.GoogleID) + readInstructor, err := readInstructor(instructor.GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading instructor: %v", err) } @@ -210,7 +264,7 @@ func TestUpdateInstructor(t *testing.T) { func TestDeleteInstructor(t *testing.T) { // Delete the instructor - err := deleteInstructor(currentUser, instructor) + err := deleteInstructor(instructor, mockRequest()) if err != nil { t.Fatalf("Error deleting instructor: %v", err) } @@ -224,13 +278,13 @@ func TestDeleteInstructor(t *testing.T) { // Testing for admin CRUD operations func TestCreateAdmin(t *testing.T) { - err := createAdmin(currentUser, admin) + err := createAdmin(admin, mockRequest()) if err != nil { t.Fatalf("Error creating admin: %v", err) } // Read the created admin - readAdmin, err := readAdmin(currentUser, admin.GoogleID) + readAdmin, err := readAdmin(admin.GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading admin: %v", err) } @@ -242,7 +296,7 @@ func TestCreateAdmin(t *testing.T) { } func TestReadAdmin(t *testing.T) { - admin, err := readAdmin(currentUser, admin.GoogleID) + admin, err := readAdmin(admin.GoogleID, mockRequest()) if err != nil { t.Fatalf("Failed to read instructor: %v", err) } @@ -258,13 +312,13 @@ func TestUpdateAdmin(t *testing.T) { "email": "amazing_admin@nk.com", } - err := updateAdmin(currentUser, admin.GoogleID, updates) + err := updateAdmin(admin.GoogleID, updates, mockRequest()) if err != nil { t.Fatalf("Error updating admin: %v", err) } // Read the updated admin - readAdmin, err := readAdmin(currentUser, admin.GoogleID) + readAdmin, err := readAdmin(admin.GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading admin: %v", err) } @@ -277,7 +331,7 @@ func TestUpdateAdmin(t *testing.T) { func TestDeleteAdmin(t *testing.T) { // Delete the admin - err := deleteAdmin(currentUser, admin) + err := deleteAdmin(admin, mockRequest()) if err != nil { t.Fatalf("Error deleting admin: %v", err) } @@ -291,13 +345,13 @@ func TestDeleteAdmin(t *testing.T) { // Testing for parent CRUD operations func TestCreateParent(t *testing.T) { - err := createParent(currentUser, parent) + err := createParent(parent, mockRequest()) if err != nil { t.Fatalf("Error creating parent: %v", err) } // Read the created parent - readParent, err := readParent(currentUser, parent.GoogleID) + readParent, err := readParent(parent.GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading parent: %v", err) } @@ -309,7 +363,7 @@ func TestCreateParent(t *testing.T) { } func TestReadParent(t *testing.T) { - parent, err := readParent(currentUser, parent.GoogleID) + parent, err := readParent(parent.GoogleID, mockRequest()) if err != nil { t.Fatalf("Failed to read parent: %v", err) } @@ -325,13 +379,13 @@ func TestUpdateParent(t *testing.T) { "email": "jane_doe_parent@nk.com", } - err := updateParent(currentUser, parent.GoogleID, updates) + err := updateParent(parent.GoogleID, updates, mockRequest()) if err != nil { t.Fatalf("Error updating parent: %v", err) } // Read the updated parent - readParent, err := readParent(currentUser, parent.GoogleID) + readParent, err := readParent(parent.GoogleID, mockRequest()) if err != nil { t.Fatalf("Error reading parent: %v", err) } @@ -344,7 +398,7 @@ func TestUpdateParent(t *testing.T) { func TestDeleteParent(t *testing.T) { // Delete the parent - err := deleteParent(currentUser, parent) + err := deleteParent(parent, mockRequest()) if err != nil { t.Fatalf("Error deleting parent: %v", err) } @@ -359,13 +413,13 @@ func TestDeleteParent(t *testing.T) { // Testing for class CRUD operations func TestCreateClass(t *testing.T) { - err := createClass(currentUser, classes[0]) + err := createClass(classes[0], mockRequest()) if err != nil { t.Fatalf("Error creating class: %v", err) } // Read the created class - readClass, err := readClass(currentUser, students, classes[0].ClassID) + readClass, err := readClass(students, classes[0].ClassID, mockRequest()) if err != nil { t.Fatalf("Error reading class: %v", err) } @@ -377,7 +431,7 @@ func TestCreateClass(t *testing.T) { } func TestReadClass(t *testing.T) { - class, err := readClass(currentUser, students, classes[0].ClassID) + class, err := readClass(students, classes[0].ClassID, mockRequest()) if err != nil { t.Fatalf("Failed to read class: %v", err) } @@ -393,13 +447,13 @@ func TestUpdateClass(t *testing.T) { "class_name": "DN", } - err := updateClass(currentUser, classes[0], updates) + err := updateClass(classes[0], updates, mockRequest()) if err != nil { t.Fatalf("Error updating class: %v", err) } // Read the updated class - readClass, err := readClass(currentUser, students, classes[0].ClassID) + readClass, err := readClass(students, classes[0].ClassID, mockRequest()) if err != nil { t.Fatalf("Error reading class: %v", err) } @@ -412,7 +466,7 @@ func TestUpdateClass(t *testing.T) { func TestDeleteClass(t *testing.T) { // Delete the class - err := deleteClass(currentUser, classes[0]) + err := deleteClass(classes[0], mockRequest()) if err != nil { t.Fatalf("Error deleting class: %v", err) } @@ -425,25 +479,25 @@ func TestDeleteClass(t *testing.T) { } func TestCreateAnnouncement(t *testing.T) { - err := createAnnouncement(currentUser, announcement) + err := createAnnouncement(announcement, mockRequest()) if err != nil { t.Fatalf("Error creating announcement: %v", err) } // Read the announcement - readAnnouncement, err := readAnnouncement(currentUser, announcement) + readAnnouncement, err := readAnnouncement(announcement.AnnouncementID, mockRequest()) if err != nil { t.Fatalf("Error reading announcement: %v", err) } // Assert that the created and read announcement are equal if !reflect.DeepEqual(announcement, readAnnouncement) { - t.Error("Created and read announcements are not equal") + t.Errorf("Created and read announcements are not equal. Expected: %+v, Got: %+v", announcement, readAnnouncement) } } func TestReadAnnouncement(t *testing.T) { - announcement, err := readAnnouncement(currentUser, announcement) + announcement, err := readAnnouncement(announcement.AnnouncementID, mockRequest()) if err != nil { t.Fatalf("Failed to read announcement: %v", err) } @@ -459,13 +513,13 @@ func TestUpdateAnnouncement(t *testing.T) { "content": "This is an updated announcement.", } - err := updateAnnouncement(currentUser, announcement, updates) + err := updateAnnouncement(announcement, updates, mockRequest()) if err != nil { t.Fatalf("Error updating announcement: %v", err) } // Read the updated announcement - readAnnouncement, err := readAnnouncement(currentUser, announcement) + readAnnouncement, err := readAnnouncement(announcement.AnnouncementID, mockRequest()) if err != nil { t.Fatalf("Error reading announcement: %v", err) } @@ -478,7 +532,7 @@ func TestUpdateAnnouncement(t *testing.T) { func TestDeleteAnnouncement(t *testing.T) { // Delete the announcement - err := deleteAnnouncement(currentUser, announcement) + err := deleteAnnouncement(announcement, mockRequest()) if err != nil { t.Fatalf("Error deleting announcement: %v", err) } diff --git a/json_validator.go b/json_validator.go deleted file mode 100644 index 7e00390..0000000 --- a/json_validator.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "encoding/json" - // "fmt" - "io" - - // "log" - "os" -) - -// func validateJSON(jsonFilePath string) { -// // Open the JSON file -// jsonFile, err := os.Open(jsonFilePath) -// if err != nil { -// log.Fatalf("Error opening JSON file: %v", err) -// } -// defer jsonFile.Close() - -// // Read the JSON file -// jsonBytes, err := io.ReadAll(jsonFile) -// if err != nil { -// log.Fatalf("Error reading JSON file: %v", err) -// } - -// // Validate JSON format -// var jsonData map[string]interface{} -// if err := json.Unmarshal(jsonBytes, &jsonData); err != nil { -// log.Fatalf("Error unmarshalling JSON: %v", err) -// } - -// fmt.Println("JSON is valid and correctly formatted.") -// } - -func validateJSON(jsonFilePath string) error { - // Open the JSON file - jsonFile, err := os.Open(jsonFilePath) - if err != nil { - return err - } - defer jsonFile.Close() - - // Read the JSON file - jsonData, err := io.ReadAll(jsonFile) - if err != nil { - return err - } - - // Validate JSON format - var parsedData interface{} - if err := json.Unmarshal(jsonData, &parsedData); err != nil { - return err - } - - // Additional validation logic can be added here - - return nil -}