class LdapService < ApplicationService def initialize @suffix = ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org" end def add_entry(dn, attrs, interactive=false) puts "Adding entry: #{dn}" if interactive res = ldap_client.add dn: dn, attributes: attrs puts res.inspect if interactive && !res res end def add_attribute(dn, attr, value) ldap_client.add_attribute dn, attr, value end def delete_entry(dn, interactive=false) puts "Deleting entry: #{dn}" if interactive res = ldap_client.delete dn: dn puts res.inspect if interactive && !res res 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 = ldap_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 admin} filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*") entries = ldap_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, admin: e.try(:admin) ? 'admin' : nil # password: e.userpassword.first } 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 = ldap_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 ldap_client ldap_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