Compare commits
4 Commits
778babcc05
...
537e1a4774
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
537e1a4774
|
||
|
|
c3b9ff8b4a
|
||
|
|
93d56f79d5
|
||
|
|
1a30345f46
|
@@ -1,4 +1,11 @@
|
||||
LDAP_HOST=localhost
|
||||
LDAP_PORT=389
|
||||
LDAP_ADMIN_PASSWORD=passthebutter
|
||||
LDAP_SUFFIX="dc=kosmos,dc=org"
|
||||
|
||||
EJABBERD_API_URL='https://xmpp.kosmos.org/api'
|
||||
|
||||
BTCPAY_API_URL='http://localhost:23001/api/v1'
|
||||
|
||||
LNDHUB_API_URL='http://localhost:3023'
|
||||
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
|
||||
|
||||
1
Gemfile
1
Gemfile
@@ -50,6 +50,7 @@ group :development, :test do
|
||||
# Use sqlite3 as the database for Active Record
|
||||
gem 'sqlite3', '~> 1.4'
|
||||
gem 'rspec-rails'
|
||||
gem "byebug", "~> 11.1"
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
||||
@@ -71,6 +71,7 @@ GEM
|
||||
bcrypt (3.1.18)
|
||||
bindex (0.8.1)
|
||||
builder (3.2.4)
|
||||
byebug (11.1.3)
|
||||
capybara (3.38.0)
|
||||
addressable
|
||||
matrix
|
||||
@@ -157,6 +158,7 @@ GEM
|
||||
matrix (0.4.2)
|
||||
method_source (1.0.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.8.0)
|
||||
minitest (5.16.3)
|
||||
net-imap (0.3.1)
|
||||
net-protocol
|
||||
@@ -168,6 +170,9 @@ GEM
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.9)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.9-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
orm_adapter (0.5.0)
|
||||
@@ -259,6 +264,8 @@ GEM
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
sqlite3 (1.5.4)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
sqlite3 (1.5.4-x86_64-linux)
|
||||
stimulus-rails (1.2.1)
|
||||
railties (>= 6.0.0)
|
||||
@@ -298,6 +305,7 @@ PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
byebug (~> 11.1)
|
||||
capybara
|
||||
cssbundling-rails
|
||||
database_cleaner
|
||||
|
||||
25
README.md
25
README.md
@@ -31,10 +31,31 @@ Running all specs:
|
||||
|
||||
bundle exec rspec
|
||||
|
||||
### Docker (Compose)
|
||||
|
||||
There is a working Dockr Compose config file, which allows you to spin up both
|
||||
an app server for Rails as well as a local 389ds (LDAP) server.
|
||||
|
||||
By default, `docker-compose up` will only start the LDAP server, listening on
|
||||
port 389 on your machine. Uncomment other services in `docker-compose.yml`.
|
||||
|
||||
### LDAP server
|
||||
|
||||
TODO make it easy to run a local Kosmos LDAP server for development, without
|
||||
manual LDIF imports etc. (or provide a staging instance)
|
||||
See the previous section for quickly spinning up an LDAP server with Docker (or
|
||||
edit your environment configuration to use an existing one).
|
||||
|
||||
After creating the Docker container for the first time (or after deleting it),
|
||||
you need to run the following command once, in order to create the dirsrv
|
||||
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
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class CreateLdapUserJob < ApplicationJob
|
||||
def ldap_client
|
||||
ldap_client ||= Net::LDAP.new host: ldap_config['host'],
|
||||
port: ldap_config['port'],
|
||||
encryption: ldap_config['ssl'],
|
||||
# encryption: ldap_config['ssl'],
|
||||
auth: {
|
||||
method: :simple,
|
||||
username: ldap_config['admin_user'],
|
||||
|
||||
111
app/services/ldap_service.rb
Normal file
111
app/services/ldap_service.rb
Normal file
@@ -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
|
||||
@@ -29,7 +29,7 @@ development:
|
||||
host: <%= ENV["LDAP_HOST"] || "localhost" %>
|
||||
port: <%= ENV["LDAP_PORT"] || "389" %>
|
||||
attribute: cn
|
||||
base: ou=kosmos.org,cn=users,dc=kosmos,dc=org
|
||||
base: <%= ENV["LDAP_BASE"] || "ou=kosmos.org,cn=users,dc=kosmos,dc=org" %>
|
||||
admin_user: "cn=Directory Manager"
|
||||
admin_password: <%= ENV["LDAP_ADMIN_PASSWORD"] %>
|
||||
ssl: <%= ENV["LDAP_USE_TLS"] || "false" %>
|
||||
|
||||
31
db/schema.rb
31
db/schema.rb
@@ -2,25 +2,24 @@
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
#
|
||||
# This file is the source Rails uses to define your schema when running `rails
|
||||
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
||||
# This file is the source Rails uses to define your schema when running `bin/rails
|
||||
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
||||
# be faster and is potentially less error prone than running all of your
|
||||
# migrations from scratch. Old migrations may fail to apply correctly if those
|
||||
# migrations use external dependencies or application code.
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_11_20_010540) do
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2021_11_20_010540) do
|
||||
create_table "donations", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "amount_sats"
|
||||
t.integer "amount_eur"
|
||||
t.integer "amount_usd"
|
||||
t.string "public_name"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.datetime "paid_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "paid_at", precision: nil
|
||||
t.index ["user_id"], name: "index_donations_on_user_id"
|
||||
end
|
||||
|
||||
@@ -28,22 +27,24 @@ ActiveRecord::Schema.define(version: 2021_11_20_010540) do
|
||||
t.string "token"
|
||||
t.integer "user_id"
|
||||
t.integer "invited_user_id"
|
||||
t.datetime "used_at"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.datetime "used_at", precision: nil
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["invited_user_id"], name: "index_invitations_on_invited_user_id"
|
||||
t.index ["user_id"], name: "index_invitations_on_user_id"
|
||||
end
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "cn"
|
||||
t.string "ou"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "email", default: "", null: false
|
||||
t.string "reset_password_token"
|
||||
t.datetime "reset_password_sent_at"
|
||||
t.datetime "reset_password_sent_at", precision: nil
|
||||
t.string "confirmation_token"
|
||||
t.datetime "confirmed_at"
|
||||
t.datetime "confirmation_sent_at"
|
||||
t.datetime "confirmed_at", precision: nil
|
||||
t.datetime "confirmation_sent_at", precision: nil
|
||||
t.string "unconfirmed_email"
|
||||
t.text "ln_login_ciphertext"
|
||||
t.text "ln_password_ciphertext"
|
||||
|
||||
@@ -3,22 +3,32 @@ services:
|
||||
image: 4teamwork/389ds:latest
|
||||
volumes:
|
||||
- ./tmp/389ds:/data
|
||||
ports:
|
||||
- "389:3389"
|
||||
environment:
|
||||
DS_DM_PASSWORD: passthebutter
|
||||
SUFFIX_NAME: "dc=kosmos,dc=org"
|
||||
web:
|
||||
build: .
|
||||
tty: true
|
||||
command: bash -c "rm -f tmp/pids/server.pid && bin/dev"
|
||||
volumes:
|
||||
- .:/akkounts
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
RAILS_ENV: development
|
||||
LDAP_HOST: ldap
|
||||
LDAP_PORT: 3389
|
||||
LDAP_ADMIN_PASSWORD: passthebutter
|
||||
LDAP_USE_TLS: "false"
|
||||
depends_on:
|
||||
- ldap
|
||||
# phpldapadmin:
|
||||
# image: osixia/phpldapadmin:0.9.0
|
||||
# ports:
|
||||
# - "8389:80"
|
||||
# environment:
|
||||
# PHPLDAPADMIN_HTTPS: false
|
||||
# PHPLDAPADMIN_LDAP_HOSTS: "#PYTHON2BASH:[{'ldap': [{'server': [{'tls': False}, {'port': 3389}]}, {'login': [{'bind_id': 'cn=Directory Manager'}, {'bind_pass': 'passthebutter'}]}]}]"
|
||||
# PHPLDAPADMIN_LDAP_CLIENT_TLS: false
|
||||
# web:
|
||||
# build: .
|
||||
# tty: true
|
||||
# command: bash -c "sleep 5 && rm -f tmp/pids/server.pid && bin/dev"
|
||||
# volumes:
|
||||
# - .:/akkounts
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
# environment:
|
||||
# RAILS_ENV: development
|
||||
# LDAP_HOST: ldap
|
||||
# LDAP_PORT: 3389
|
||||
# LDAP_ADMIN_PASSWORD: passthebutter
|
||||
# LDAP_USE_TLS: "false"
|
||||
# depends_on:
|
||||
# - ldap
|
||||
|
||||
24
lib/tasks/ldap.rake
Normal file
24
lib/tasks/ldap.rake
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user