diff --git a/README.md b/README.md index 5b249c4..598ae74 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,13 @@ back-end: docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev" +Now you can seed the back-end with data using this Rails task: + + bundle exec rails ldap:seed + +The seeds task will first delete any existing entries in the directory tree +("dc=kosmos,dc=org"), and then create our example/development entries. + ## Documentation * [Ruby on Rails](https://guides.rubyonrails.org/) diff --git a/app/services/ldap_service.rb b/app/services/ldap_service.rb new file mode 100644 index 0000000..713360a --- /dev/null +++ b/app/services/ldap_service.rb @@ -0,0 +1,111 @@ +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 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", "*") + + 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 + + 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 diff --git a/lib/tasks/ldap.rake b/lib/tasks/ldap.rake new file mode 100644 index 0000000..827b011 --- /dev/null +++ b/lib/tasks/ldap.rake @@ -0,0 +1,24 @@ +namespace :ldap do + desc "Set up base entries for LDAP directory" + task seed: :environment do |t, args| + ldap = LdapService.new + + ldap.delete_all_entries + + ldap.add_entry "dc=kosmos,dc=org", { + dc: "kosmos", objectClass: ["top", "domain"] + }, true + ldap.add_entry "cn=users,dc=kosmos,dc=org", { + cn: "users", objectClass: ["top", "organizationalRole"] + }, true + + ldap.add_organization "kosmos.org", "Kosmos", true + end + + desc "List user domains/organizations" + task list_organizations: :environment do |t, args| + ldap = LdapService.new + orgs = ldap.fetch_organizations + puts orgs.inspect + end +end