# Mentorenwahl: A fullstack application for assigning mentors to students based on their whishes. # Copyright (C) 2022 Dominic Grimm # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . require "ldap" require "uuid" require "uuid/json" require "random/secure" module Backend module Api module Schema @[GraphQL::Object] class Mutation < GraphQL::BaseMutation @[GraphQL::Field] # Logs in as *username* with credential *password* def login(username : String, password : String) : LoginPayload? raise Errors::Authentication.new if username.empty? || password.empty? user = Db::User.query.find { var(:username) == username } raise Errors::Authentication.new unless user && Ldap.authenticate?(Ldap::DN.uid(username), password) token = Db::Token.create!( id: UUID.random(Random::Secure), iat: Time.utc, exp: Time.utc + Backend.config.api.jwt_expiration.minutes, active: true, user_id: user.id ) LoginPayload.new( user: User.new(user), token: Auth::Token.new( iat: token.iat.to_unix, exp: token.exp.to_unix, jti: token.id.not_nil!, context: Auth::Context.new(user.id.not_nil!) ).encode ) end @[GraphQL::Field] # Logs out of account by revoking token def logout(context : Context) : Scalars::UUID? context.authenticated! jti = context.jti.not_nil! token = Db::Token.find!(jti) token.active = false token.save! Scalars::UUID.new(jti) end @[GraphQL::Field] def revoke_token(context : Context, token : Scalars::UUID) : Scalars::UUID context.authenticated! Db::Token.query.find!(token.value).delete Scalars::UUID.new(token.value) end @[GraphQL::Field] # Creates user def create_user(context : Context, input : UserCreateInput, check_ldap : Bool = true) : User? context.admin! raise Errors::LdapUserDoesNotExist.new if check_ldap && begin !Ldap::User.from_username(input.username) rescue LDAP::Client::AuthError true end user = Db::User.create!(username: input.username, role: input.role.to_db, admin: input.admin) Worker::Jobs::CacheLdapUserJob.new(user.id.not_nil!.to_i).enqueue User.new(user) end @[GraphQL::Field] # Deletes user by ID def delete_user(context : Context, id : Int32) : Int32? context.admin! user = Db::User.find!(id) user.delete id end @[GraphQL::Field] # Starts assignment job of mentors to students def start_assignment(context : Context) : Bool? context.admin! Worker::Jobs::AssignStudentsJob.new.enqueue true end # @[GraphQL::Field] # # Creates teacher # def create_teacher(context : Context, input : TeacherCreateInput) : Teacher # context.admin! # teacher = Db::Teacher.create!(user_id: input.user_id, max_students: input.max_students) # Teacher.new(teacher) # end @[GraphQL::Field] def register_teacher(context : Context, input : TeacherInput) : Teacher? context.teacher!(false) raise Errors::InvalidPermissions.new if context.user.not_nil!.teacher teacher = Db::Teacher.create!(user_id: context.user.not_nil!.id, max_students: input.max_students) Teacher.new(teacher) end # @[GraphQL::Field] # # Deletes teacher by ID # def delete_teacher(context : Context, id : Int32) : Int32 # context.admin! # teacher = Db::Teacher.find!(id) # teacher.delete # id # end # @[GraphQL::Field] # # Self register as teacher # def register_teacher(context : Context, input : TeacherInput) : Teacher # context.teacher! external_check: false # Teacher.new( # Db::Teacher.create!(user_id: context.user.not_nil!.id, max_students: input.max_students) # ) # end # @[GraphQL::Field] # # Creates student # def create_student(context : Context, input : StudentCreateInput) : Student # context.admin! # user = Db::User.find!(input.user_id) # raise "User not a student" unless user.role.to_api.student? # student = Db::Student.create!(user_id: user.id) # Student.new(student) # end # @[GraphQL::Field] # # Deletes student by ID # def delete_student(context : Context, id : Int32) : Int32 # context.admin! # student = Db::Student.find!(id) # student.delete # id # end # @[GraphQL::Field] # # Self register as student # def register_student(context : Context) : Student # context.student! external_check: false # Student.new( # Db::Student.create!(user_id: context.user.not_nil!.id) # ) # end @[GraphQL::Field] # Creates vote for authenticated user's student def create_vote(context : Context, input : VoteCreateInput) : Vote? context.student! raise Errors::DuplicateTeachers.new if input.teacher_ids.uniq.size != input.teacher_ids.size raise Errors::NotEnoughTeachers.new if input.teacher_ids.size < Backend.config.minimum_teacher_selection_count teacher_role_count = Db::User.query.where(role: Db::UserRole::Teacher).count raise Errors::TeachersNotRegistered.new if teacher_role_count != Db::Teacher.query.count || teacher_role_count.zero? input.teacher_ids.each do |id| teacher = Db::Teacher.find(id) if teacher.nil? raise Errors::TeachersNotFound.new # elsif teacher.user.skif != context.user.not_nil!.skif # if teacher.user.skif # raise "Teacher is SKIF, student is not" # else # raise "Teacher is not SKIF, student is" # end end end student = context.external.not_nil!.as(Db::Student) vote = Db::Vote.create!(student_id: student.id) Db::TeacherVote.import(input.teacher_ids.map_with_index { |id, i| Db::TeacherVote.new({vote_id: vote.id, teacher_id: id, priority: i}) }) Vote.new(vote) end end end end end