160 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
class LdapService < ApplicationService
 | 
						|
  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
 | 
						|
 | 
						|
  def delete_all_users!
 | 
						|
    delete_all_entries!(objectclass: "person")
 | 
						|
  end
 | 
						|
 | 
						|
  def delete_all_entries!(objectclass: "*")
 | 
						|
    if Rails.env.production?
 | 
						|
      raise "Mass deletion of entries not allowed in production"
 | 
						|
    end
 | 
						|
 | 
						|
    filter  = Net::LDAP::Filter.eq("objectClass", objectclass)
 | 
						|
    entries = client.search(base: ldap_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,#{ldap_suffix}"
 | 
						|
    else
 | 
						|
      treebase = ldap_config["base"]
 | 
						|
    end
 | 
						|
 | 
						|
    attributes = %w[
 | 
						|
      dn cn uid mail displayName admin serviceEnabled
 | 
						|
      mailRoutingAddress mailpassword nostrKey pgpKey
 | 
						|
    ]
 | 
						|
    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,
 | 
						|
        services_enabled: e.try(:serviceEnabled),
 | 
						|
        email_maildrop: e.try(:mailRoutingAddress),
 | 
						|
        email_password: e.try(:mailpassword),
 | 
						|
        nostr_key: e.try(:nostrKey) ? e.nostrKey.first : nil,
 | 
						|
        pgp_key: e.try(:pgpKey) ? e.pgpKey.first : nil
 | 
						|
      }
 | 
						|
    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,#{ldap_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,#{ldap_suffix}"
 | 
						|
 | 
						|
    aci = <<-EOS
 | 
						|
(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || userPassword || mail || mailRoutingAddress || serviceEnabled || nostrKey || pgpKey || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_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:///#{ldap_suffix}")(targetattr="*") (version 3.0; acl "user-read-search-own-attributes"; allow (read,search) userdn="ldap:///self";)
 | 
						|
    EOS
 | 
						|
 | 
						|
    add_entry ldap_suffix, {
 | 
						|
      dc: "kosmos", objectClass: ["top", "domain"], aci: user_read_aci
 | 
						|
    }, true
 | 
						|
 | 
						|
    add_entry "cn=users,#{ldap_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
 | 
						|
 | 
						|
  def ldap_suffix
 | 
						|
    @ldap_suffix ||= ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org"
 | 
						|
  end
 | 
						|
end
 |