class DepartmentsController < ApplicationController layout 'admin' before_action :require_admin before_action :find_department, only: [:show, :edit, :update, :destroy, :add_member, :remove_member, :set_leader, :set_acting_leader, :clear_leader] def index @departments = Department.roots.sorted.includes(:children, :leader, :acting_leader, :users) end def show @members = @department.department_members.includes(:user).order(:position) end def new @department = Department.new @department.parent_id = params[:parent_id] if params[:parent_id] @department.department_type = params[:type] || 'team' load_parent_options end def create @department = Department.new(department_params) if @department.save flash[:notice] = l(:notice_successful_create) redirect_to '/org_chart' else load_parent_options render :new end end def edit load_parent_options end def update if @department.update(department_params) flash[:notice] = l(:notice_successful_update) redirect_to '/org_chart' else load_parent_options render :edit end end def destroy @department.destroy flash[:notice] = l(:notice_successful_delete) redirect_to '/org_chart' end def add_member user = find_or_create_user_from_ldap(params[:user_id], params[:ldap_uid], params[:ldap_id]) if user member = @department.department_members.find_or_initialize_by(user: user) member.role = 'member' if member.save flash[:notice] = "#{user.name} added to #{@department.name}" else flash[:error] = member.errors.full_messages.join(', ') end else flash[:error] = 'User not found' end redirect_to department_path(@department) end def remove_member member = @department.department_members.find_by(user_id: params[:user_id]) if member @department.update(leader_id: nil) if @department.leader_id == member.user_id @department.update(acting_leader_id: nil) if @department.acting_leader_id == member.user_id member.destroy flash[:notice] = 'Member removed' end redirect_to department_path(@department) end def set_leader user = User.find(params[:user_id]) @department.department_members.find_or_create_by(user: user) @department.update(leader_id: user.id, acting_leader_id: nil) flash[:notice] = "#{user.name} is now the leader" redirect_to department_path(@department) end def set_acting_leader user = User.find(params[:user_id]) @department.department_members.find_or_create_by(user: user) @department.update(acting_leader_id: user.id) flash[:notice] = "#{user.name} is now the acting leader" redirect_to department_path(@department) end def clear_leader @department.update(leader_id: nil, acting_leader_id: nil) flash[:notice] = 'Leader cleared' redirect_to department_path(@department) end def org_chart @ceo = Department.ceo.first @departments = Department.roots.sorted.includes(:children, :leader, :acting_leader, :users) render layout: 'base' end def search_ldap query = params[:q].to_s.strip if query.length < 2 render json: [] return end results = [] AuthSourceLdap.all.each do |ldap| begin ldap_users = search_ldap_users(ldap, query) ldap_users.each do |entry| uid = entry[ldap.attr_login]&.first next unless uid existing_user = User.find_by(login: uid) results << { uid: uid, name: "#{entry[ldap.attr_firstname]&.first} #{entry[ldap.attr_lastname]&.first}".strip, email: entry[ldap.attr_mail]&.first, ldap_id: ldap.id, exists: existing_user.present?, user_id: existing_user&.id } end rescue => e Rails.logger.error "LDAP search error: #{e.message}" end end render json: results.uniq { |r| r[:uid] }.first(20) end private def find_department @department = Department.find(params[:id]) rescue ActiveRecord::RecordNotFound render_404 end def department_params params.require(:department).permit(:name, :parent_id, :position, :description, :department_type, :leader_id, :acting_leader_id) end def load_parent_options if @department.new_record? @parents = @department.available_parents.sorted else @parents = @department.available_parents.where.not(id: @department.self_and_descendants.map(&:id)).sorted end end def search_ldap_users(ldap, query) options = { host: ldap.host, port: ldap.port, auth: { method: :simple, username: ldap.account, password: ldap.account_password } } if ldap.tls if ldap.verify_peer options[:encryption] = { method: :simple_tls } else options[:encryption] = { method: :simple_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } end end conn = Net::LDAP.new(options) filter = Net::LDAP::Filter.begins(ldap.attr_login, query) | Net::LDAP::Filter.begins(ldap.attr_firstname, query) | Net::LDAP::Filter.begins(ldap.attr_lastname, query) if ldap.filter.present? base_filter = Net::LDAP::Filter.construct(ldap.filter) filter = base_filter & filter end conn.search( base: ldap.base_dn, filter: filter, attributes: [ldap.attr_login, ldap.attr_firstname, ldap.attr_lastname, ldap.attr_mail], size: 20 ) || [] end def find_or_create_user_from_ldap(user_id, ldap_uid, ldap_id) return User.find(user_id) if user_id.present? return nil if ldap_uid.blank? user = User.find_by(login: ldap_uid) return user if user ldap = AuthSourceLdap.find_by(id: ldap_id) return nil unless ldap user_info = get_ldap_user_info(ldap, ldap_uid) return nil unless user_info user = User.new( login: ldap_uid, firstname: user_info[:firstname] || ldap_uid, lastname: user_info[:lastname] || '-', mail: user_info[:mail] || "#{ldap_uid}@example.com", auth_source_id: ldap.id, status: User::STATUS_ACTIVE ) user.random_password user.save ? user : nil end def get_ldap_user_info(ldap, uid) options = { host: ldap.host, port: ldap.port, auth: { method: :simple, username: ldap.account, password: ldap.account_password } } if ldap.tls if ldap.verify_peer options[:encryption] = { method: :simple_tls } else options[:encryption] = { method: :simple_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } end end conn = Net::LDAP.new(options) filter = Net::LDAP::Filter.eq(ldap.attr_login, uid) if ldap.filter.present? base_filter = Net::LDAP::Filter.construct(ldap.filter) filter = base_filter & filter end result = conn.search( base: ldap.base_dn, filter: filter, attributes: [ldap.attr_login, ldap.attr_firstname, ldap.attr_lastname, ldap.attr_mail], size: 1 )&.first return nil unless result { firstname: result[ldap.attr_firstname]&.first, lastname: result[ldap.attr_lastname]&.first, mail: result[ldap.attr_mail]&.first } end end