143 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| class LdapService < ApplicationService
 | |
|   def initialize
 | |
|     @suffix = ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org"
 | |
|   end
 | |
| 
 | |
|   def add_attribute(dn, attr, values)
 | |
|     ldap_client.add_attribute dn, attr, values
 | |
|   end
 | |
| 
 | |
|   def replace_attribute(dn, attr, values)
 | |
|     ldap_client.replace_attribute dn, attr, values
 | |
|   end
 | |
| 
 | |
|   def delete_attribute(dn, attr)
 | |
|     ldap_client.delete_attribute dn, attr
 | |
|   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 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 displayName admin service}
 | |
|     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,
 | |
|         display_name: e.try(:displayName) ? e.displayName.first : nil,
 | |
|         admin: e.try(:admin) ? 'admin' : nil,
 | |
|         service: e.try(:service)
 | |
|       }
 | |
|     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
 |