class LdapService < ApplicationService def initialize @suffix = ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org" end def modify(dn, operations=[]) client.modify dn: dn, operations: operations client.get_operation_result.code end def add_attribute(dn, attr, values) client.add_attribute dn, attr, values client.get_operation_result.code end def replace_attribute(dn, attr, values) client.replace_attribute dn, attr, values client.get_operation_result.code end def delete_attribute(dn, attr) client.delete_attribute dn, attr client.get_operation_result.code end def add_entry(dn, attrs, interactive=false) puts "Add entry: #{dn}" if interactive client.add dn: dn, attributes: attrs client.get_operation_result.code end def delete_entry(dn, interactive=false) puts "Delete entry: #{dn}" if interactive client.delete dn: dn client.get_operation_result.code end end def delete_all_entries! if Rails.env.production? raise "Mass deletion of entries not allowed in production" end filter = Net::LDAP::Filter.eq("objectClass", "*") entries = client.search(base: @suffix, filter: filter, attributes: %w{dn}) entries.sort_by!{ |e| e.dn.length }.reverse! entries.each do |e| delete_entry e.dn, true end end def fetch_users(args={}) if args[:ou] treebase = "ou=#{args[:ou]},cn=users,#{@suffix}" else treebase = ldap_config["base"] end attributes = %w[ dn cn uid mail displayName admin service mailRoutingAddress mailpassword ] filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*") entries = client.search(base: treebase, filter: filter, attributes: attributes) entries.sort_by! { |e| e.cn[0] } entries = entries.collect do |e| { uid: e.uid.first, mail: e.try(:mail) ? e.mail.first : nil, display_name: e.try(:displayName) ? e.displayName.first : nil, admin: e.try(:admin) ? 'admin' : nil, service: e.try(:service), email_maildrop: e.try(:mailRoutingAddress), email_password: e.try(:mailpassword) } end end def fetch_organizations attributes = %w{dn ou description} filter = Net::LDAP::Filter.eq("objectClass", "organizationalUnit") # filter = Net::LDAP::Filter.eq("objectClass", "*") treebase = "cn=users,#{@suffix}" entries = client.search(base: treebase, filter: filter, attributes: attributes) entries.sort_by! { |e| e.ou[0] } entries = entries.collect do |e| { dn: e.dn, ou: e.ou.first, description: e.try(:description) ? e.description.first : nil, } end end def add_organization(ou, description, interactive=false) dn = "ou=#{ou},cn=users,#{@suffix}" aci = <<-EOS (target="ldap:///cn=*,ou=#{ou},cn=users,#{@suffix}")(targetattr="cn || sn || uid || mail || userPassword || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{@suffix}";) EOS attrs = { objectClass: ["top", "organizationalUnit"], description: description, ou: ou, aci: aci } add_entry dn, attrs, interactive end def reset_directory! if Rails.env.production? raise "Resetting the directory not allowed in production" end delete_all_entries! user_read_aci = <<-EOS (target="ldap:///#{@suffix}")(targetattr="*") (version 3.0; acl "user-read-search-own-attributes"; allow (read,search) userdn="ldap:///self";) EOS add_entry @suffix, { dc: "kosmos", objectClass: ["top", "domain"], aci: user_read_aci }, true add_entry "cn=users,#{@suffix}", { cn: "users", objectClass: ["top", "organizationalRole"] }, true end private def client client ||= Net::LDAP.new host: ldap_config['host'], port: ldap_config['port'], # TODO has to be :simple_tls if TLS is enabled # encryption: ldap_config['ssl'], auth: { method: :simple, username: ldap_config['admin_user'], password: ldap_config['admin_password'] } end def ldap_config ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env] end end