diff --git a/docker/backend/shard.lock b/docker/backend/shard.lock index 1a0e96b..dc8b1fd 100644 --- a/docker/backend/shard.lock +++ b/docker/backend/shard.lock @@ -62,7 +62,7 @@ shards: mosquito: git: https://github.com/mosquito-cr/mosquito.git - version: 0.11.2 + version: 1.0.0.rc1+git.commit.afd53dd241447b60ece9232b6c71669b192baaa4 openssl_ext: git: https://github.com/spider-gazelle/openssl_ext.git diff --git a/docker/backend/shard.yml b/docker/backend/shard.yml index ddc93b4..d167d61 100644 --- a/docker/backend/shard.yml +++ b/docker/backend/shard.yml @@ -46,6 +46,7 @@ dependencies: github: juanedi/micrate mosquito: github: mosquito-cr/mosquito + branch: master quartz_mailer: github: amberframework/quartz-mailer kilt: diff --git a/docker/backend/src/backend/api/schema/helpers.cr b/docker/backend/src/backend/api/schema/helpers.cr index c611bdf..513473d 100644 --- a/docker/backend/src/backend/api/schema/helpers.cr +++ b/docker/backend/src/backend/api/schema/helpers.cr @@ -19,39 +19,33 @@ module Backend module Schema # Schema helper macros module Helpers - # Object helpers - module ObjectMacros - # Defines field property and GraphQL specific getter - macro field(type) - property {{ type.var }} {% if type.value %} = {{ type.value }}{% end %} + # Defines field property and GraphQL specific getter + macro field(type) + property {{ type.var }} {% if type.value %} = {{ type.value }}{% end %} - @[GraphQL::Field] - def {{ type.var }} : {{ type.type }} - @{{ type.var }} - end + @[GraphQL::Field] + def {{ type.var }} : {{ type.type }} + @{{ type.var }} end end - # DB model field helpers - module DbObject - # Defines DB model field helper functions - macro db_object(type) - private property model + # Defines DB model field helper functions + macro db_object(type) + private property model - def initialize(@model : {{ type }}) - end + def initialize(@model : {{ type }}) + end - def initialize(id : Int32) - @model = {{ type }}.find!(id) - end + def initialize(id : Int32) + @model = {{ type }}.find!(id) + end - {% space_name = type.names.last.underscore.gsub(/_/, " ").capitalize %} + {% space_name = type.names.last.underscore.gsub(/_/, " ").capitalize %} - @[GraphQL::Field] - # {{ space_name }}'s ID - def id : Int32 - @model.id.not_nil!.to_i - end + @[GraphQL::Field] + # {{ space_name }}'s ID + def id : Int32 + @model.id.not_nil!.to_i end end end diff --git a/docker/backend/src/backend/api/schema/mutation.cr b/docker/backend/src/backend/api/schema/mutation.cr index fcd75a5..6fce16d 100644 --- a/docker/backend/src/backend/api/schema/mutation.cr +++ b/docker/backend/src/backend/api/schema/mutation.cr @@ -44,16 +44,12 @@ module Backend context.admin! raise "LDAP user does not exist" if check_ldap && begin - !Ldap.user(Ldap::Constructor.uid(input.username)) + !Ldap::User.from_username(input.username) rescue LDAP::Client::AuthError true end user = Db::User.create!(username: input.username, role: input.role.to_s, admin: input.admin) - Redis::CLIENT.set( - "ldap:user:#{user.id.not_nil!}", - Ldap.user(Ldap::Constructor.uid(user.username)).to_json, - (Backend.config.ldap.cache_refresh_interval * 2).minutes.to_i - ) + Worker::Jobs::CacheLdapUserJob.new(user.id.not_nil!.to_i).enqueue User.new(user) end diff --git a/docker/backend/src/backend/api/schema/student.cr b/docker/backend/src/backend/api/schema/student.cr index bfce6eb..5dad9a7 100644 --- a/docker/backend/src/backend/api/schema/student.cr +++ b/docker/backend/src/backend/api/schema/student.cr @@ -20,7 +20,7 @@ module Backend @[GraphQL::Object] # Student model class Student < GraphQL::BaseObject - include Helpers::DbObject + include Helpers db_object Db::Student diff --git a/docker/backend/src/backend/api/schema/teacher.cr b/docker/backend/src/backend/api/schema/teacher.cr index 6368a5f..7b15bab 100644 --- a/docker/backend/src/backend/api/schema/teacher.cr +++ b/docker/backend/src/backend/api/schema/teacher.cr @@ -20,7 +20,7 @@ module Backend @[GraphQL::Object] # Teacher model class Teacher < GraphQL::BaseObject - include Helpers::DbObject + include Helpers db_object Db::Teacher diff --git a/docker/backend/src/backend/api/schema/teacher_vote.cr b/docker/backend/src/backend/api/schema/teacher_vote.cr index 2d29b4d..d075de0 100644 --- a/docker/backend/src/backend/api/schema/teacher_vote.cr +++ b/docker/backend/src/backend/api/schema/teacher_vote.cr @@ -20,7 +20,7 @@ module Backend @[GraphQL::Object] # Teacher vote model class TeacherVote < GraphQL::BaseObject - include Helpers::DbObject + include Helpers db_object Db::TeacherVote diff --git a/docker/backend/src/backend/api/schema/user.cr b/docker/backend/src/backend/api/schema/user.cr index 257d257..3bbac50 100644 --- a/docker/backend/src/backend/api/schema/user.cr +++ b/docker/backend/src/backend/api/schema/user.cr @@ -20,26 +20,34 @@ module Backend @[GraphQL::Object] # User model class User < GraphQL::BaseObject - include Helpers::DbObject + include Helpers db_object Db::User + # LDAP user data + getter ldap : Ldap::User? + + # Refreshes LDAP user data + def refresh_ldap : Ldap::User + (@ldap ||= Ldap::User.from_json(Redis::CLIENT.get("ldap:user:#{id}").as(String))).not_nil! + end + @[GraphQL::Field] # User's first name def first_name : String - @model.first_name + refresh_ldap.first_name end @[GraphQL::Field] # User's last name def last_name : String - @model.last_name + refresh_ldap.last_name end @[GraphQL::Field] # User's full name def name : String - @model.name + refresh_ldap.name end @[GraphQL::Field] @@ -51,7 +59,7 @@ module Backend @[GraphQL::Field] # User's email def email : String - @model.email + refresh_ldap.email end @[GraphQL::Field] diff --git a/docker/backend/src/backend/api/schema/vote.cr b/docker/backend/src/backend/api/schema/vote.cr index af627e9..83e349e 100644 --- a/docker/backend/src/backend/api/schema/vote.cr +++ b/docker/backend/src/backend/api/schema/vote.cr @@ -20,7 +20,7 @@ module Backend @[GraphQL::Object] # Vote model class Vote < GraphQL::BaseObject - include Helpers::DbObject + include Helpers db_object Db::Vote diff --git a/docker/backend/src/backend/db/user.cr b/docker/backend/src/backend/db/user.cr index 0df968a..5e37bbf 100644 --- a/docker/backend/src/backend/db/user.cr +++ b/docker/backend/src/backend/db/user.cr @@ -20,9 +20,6 @@ module Backend class User < Granite::Base table users - # LDAP user data - getter ldap : Ldap::User? - has_one :teacher has_one :student @@ -38,31 +35,6 @@ module Backend # User is admin column admin : Bool = false - # User's first name - def first_name : String - refresh_ldap.first_name - end - - # User's last name - def last_name : String - refresh_ldap.last_name - end - - # User's full name - def name : String - "#{first_name} #{last_name}" - end - - # User's email - def email : String - refresh_ldap.email - end - - # Refreshes LDAP user data - def refresh_ldap : Ldap::User - (@ldap ||= Ldap::User.from_json(Redis::CLIENT.get("ldap:user:#{@id}").as(String))).not_nil! - end - validate :role, "needs to be a valid role" do |user| UserRole.parse(user.role).in?(UserRole.values) end diff --git a/docker/backend/src/backend/ldap.cr b/docker/backend/src/backend/ldap.cr index 18497ab..cd345f3 100644 --- a/docker/backend/src/backend/ldap.cr +++ b/docker/backend/src/backend/ldap.cr @@ -35,23 +35,6 @@ module Backend LDAP::Client.new(TCPSocket.new(Backend.config.ldap.host, Backend.config.ldap.port)) end - # Queries the LDAP server for a user - # - # NOTE: Returns raw LDAP data - def raw_user(dn : String) : User::Raw - CLIENT.connection do |client| - client - .authenticate(Backend.config.ldap.bind_dn, Backend.config.ldap.bind_password) - .search(base: dn) - .first - end - end - - # Queries the LDAP server for a user - def user(dn : String) : User - User.from_raw(raw_user(dn)) - end - # Checks if credentials are valid def authenticate?(dn : String, password : String) : Bool !!CLIENT.connection(&.authenticate(dn, password)) diff --git a/docker/backend/src/backend/ldap/user.cr b/docker/backend/src/backend/ldap/user.cr index 4989490..692831e 100644 --- a/docker/backend/src/backend/ldap/user.cr +++ b/docker/backend/src/backend/ldap/user.cr @@ -39,6 +39,11 @@ module Backend def initialize(@first_name : String, @last_name : String, @email : String) end + # Name + def name : String + "#{first_name} #{last_name}" + end + # Creates user data from LDAP entry def self.from_raw(raw : Raw) : self self.new( @@ -47,6 +52,33 @@ module Backend email: raw["mail"].first ) end + + # Creates user data from LDAP DN entry + def self.from_dn(dn : String) : self + from_raw( + CLIENT.connection do |client| + client + .authenticate(Backend.config.ldap.bind_dn, Backend.config.ldap.bind_password) + .search(base: dn, attributes: %w(givenName sn mail)) + .first + end + ) + end + + # Creates user data from LDAP username + def self.from_username(username : String) : self + from_dn(Ldap::Constructor.uid(username)) + end + + # Creates user data from DB entry + def self.from_db(user : Db::User) : self + from_username(user.username) + end + + # Creates user data from DB entry index + def self.from_index(id : Int32) : self + from_db(Db::User.find!(id)) + end end end end diff --git a/docker/backend/src/backend/mailers/teacher_registration_mailer.cr b/docker/backend/src/backend/mailers/teacher_registration_mailer.cr index 3cf46b8..7a9ee37 100644 --- a/docker/backend/src/backend/mailers/teacher_registration_mailer.cr +++ b/docker/backend/src/backend/mailers/teacher_registration_mailer.cr @@ -22,7 +22,7 @@ module Backend address email: Backend.config.smtp.username, name: Backend.config.smtp.name end - def initialize(user : Db::User) + def initialize(user : Ldap::User) to name: user.name, email: user.email subject "Mentorenwahl Lehrer Registrierung" text Kilt.render("#{__DIR__}/templates/teacher_registration_mailer.txt.ecr") diff --git a/docker/backend/src/backend/worker/jobs/cache_ldap_user_job.cr b/docker/backend/src/backend/worker/jobs/cache_ldap_user_job.cr index 8e43b30..2a2b8d9 100644 --- a/docker/backend/src/backend/worker/jobs/cache_ldap_user_job.cr +++ b/docker/backend/src/backend/worker/jobs/cache_ldap_user_job.cr @@ -19,16 +19,18 @@ module Backend module Jobs # Caches user data in redis cache class CacheLdapUserJob < Mosquito::QueuedJob - params id : Int64 + params id : Int32 # :ditto: def perform : Nil key = "ldap:user:#{id}" user = Db::User.find(id) if user - ldap_user = Ldap.user(Ldap::Constructor.uid(user.username)) - Redis::CLIENT.set(key, ldap_user.to_json, (Backend.config.ldap.cache_refresh_interval * 2).minutes.to_i) + log "Caching user ##{id}..." + ldap_user = Ldap::User.from_username(user.username) + Redis::CLIENT.set(key, ldap_user.to_json) else + log "User ##{id} not found. Deleting cache..." Redis::CLIENT.del(key) end end diff --git a/docker/backend/src/backend/worker/jobs/cache_ldap_users_job.cr b/docker/backend/src/backend/worker/jobs/cache_ldap_users_job.cr index 7dcf50e..2562e77 100644 --- a/docker/backend/src/backend/worker/jobs/cache_ldap_users_job.cr +++ b/docker/backend/src/backend/worker/jobs/cache_ldap_users_job.cr @@ -24,10 +24,16 @@ module Backend # :ditto: def perform : Nil Redis::CLIENT.keys("ldap:user:*") - .map(&.as(String).split(":")[2].to_i64) - .concat(Db::User.all.map(&.id.not_nil!)) + .map(&.as(String).split(":")[2].to_i) + .concat(Db::User.all.map(&.id.not_nil!.to_i)) .uniq! - .each { |id| CacheLdapUserJob.new(id).enqueue } + .each do |id| + spawn do + CacheLdapUserJob.new(id).enqueue + end + end + + Fiber.yield end end end diff --git a/docker/backend/src/backend/worker/jobs/send_teachers_registration_email_job.cr b/docker/backend/src/backend/worker/jobs/send_teachers_registration_email_job.cr index 0ca9548..99c98d4 100644 --- a/docker/backend/src/backend/worker/jobs/send_teachers_registration_email_job.cr +++ b/docker/backend/src/backend/worker/jobs/send_teachers_registration_email_job.cr @@ -32,8 +32,9 @@ module Backend fail end - log "Sending teacher registration email to #{user.email} (#{user.id})" - Mailers::TeacherRegistrationMailer.new(user).deliver + ldap_user = Ldap::User.from_username(user.username) + log "Sending teacher registration email to #{ldap_user.email} ##{user.id}" + Mailers::TeacherRegistrationMailer.new(ldap_user).deliver channel.send(nil) end diff --git a/docker/backend/src/cli/backend.cr b/docker/backend/src/cli/backend.cr index 62e94b0..be59c09 100644 --- a/docker/backend/src/cli/backend.cr +++ b/docker/backend/src/cli/backend.cr @@ -84,7 +84,7 @@ cli = Commander::Command.new do |cmd| abort unless (gets(chomp: true) || "").strip.downcase == "y" user = Backend::Db::User.create!(username: username, role: role.to_s, admin: opts.bool["admin"]) - Backend::Worker::Jobs::CacheLdapUserJob.new(user.id.not_nil!).enqueue + Backend::Worker::Jobs::CacheLdapUserJob.new(user.id.not_nil!.to_i).enqueue puts "Done!"