Merge remote-tracking branch 'upstream/master'

This commit is contained in:
fsight 2022-04-18 23:31:24 +05:00
commit 31f11627e1
12 changed files with 150 additions and 62 deletions

View File

@ -1,17 +1,20 @@
sudo: required
dist: focal
language: ruby
rvm:
- "2.0.0"
- "2.4.0"
- ruby-head
env:
- "PGVERSION=10.0-1-linux-x64 PATH=\"/opt/PostgreSQL/10/bin:$PATH\""
- "PGVERSION=9.3.19-1-linux-x64 PATH=\"/opt/PostgreSQL/9.3/bin:$PATH\""
- "PGVERSION=14"
- "PGVERSION=9.6"
before_install:
- gem install bundler
- gem install bundler --no-doc --conservative
- bundle install
# Download and install postgresql version to test against in /opt
- |
wget http://get.enterprisedb.com/postgresql/postgresql-$PGVERSION.run && \
chmod +x postgresql-$PGVERSION.run && \
sudo ./postgresql-$PGVERSION.run --extract-only 1 --mode unattended
# Download and install postgresql version to test against in /opt (for non-cross compile only)
- echo "deb http://apt.postgresql.org/pub/repos/apt/ ${TRAVIS_DIST}-pgdg main $PGVERSION" | sudo tee -a /etc/apt/sources.list.d/pgdg.list
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
- sudo apt -y update
- sudo apt -y --allow-downgrades install postgresql-$PGVERSION libpq-dev
- export PATH=/usr/lib/postgresql/$PGVERSION/bin:$PATH
script: rake test

View File

@ -1,3 +1,12 @@
## 0.3.0 / 2022-01-18
* Add config option :bothcase_name .
This adds both spellings "Fred_Flintstone" and "fred_flintstone" as PostgreSQL users/groups.
* Update gem dependencies
* Fix compatibility with PostgreSQL-14
* Require ruby-2.4+
## 0.2.0 / 2018-03-13
* Update gem dependencies

View File

@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.org/larskanis/pg-ldap-sync.svg?branch=master)](https://travis-ci.org/larskanis/pg-ldap-sync) [![Build status](https://ci.appveyor.com/api/projects/status/09xn9q5p64jbxtka/branch/master?svg=true)](https://ci.appveyor.com/project/larskanis/pg-ldap-sync/branch/master)
[![Build Status](https://app.travis-ci.com/larskanis/pg-ldap-sync.svg?branch=master)](https://app.travis-ci.com/larskanis/pg-ldap-sync) [![Build status](https://ci.appveyor.com/api/projects/status/09xn9q5p64jbxtka/branch/master?svg=true)](https://ci.appveyor.com/project/larskanis/pg-ldap-sync/branch/master)
# Use LDAP permissions in PostgreSQL

View File

@ -1,3 +1,5 @@
image: Visual Studio 2019
init:
- set PATH=C:/Ruby%ruby_version%/bin;c:/Program Files/Git/cmd;c:/Windows/system32;C:/Windows/System32/WindowsPowerShell/v1.0
- set RUBYOPT=--verbose
@ -6,7 +8,7 @@ install:
- ver
- ruby --version
- gem --version
- gem install bundler --conservative
- gem install bundler --no-doc --conservative
- bundle install
build_script:
@ -19,7 +21,7 @@ test_script:
environment:
matrix:
- ruby_version: "25-x64"
PGVER: 10
- ruby_version: "22"
- ruby_version: "27-x64"
PGVER: 13
- ruby_version: "24"
PGVER: 10

View File

@ -1,6 +1,6 @@
# With this sample config the distinction between LDAP-synchronized
# groups/users from is done by the membership to ldap_user and
# ldap_group. These two roles has to be defined manally before
# ldap_group. These two roles have to be defined manally before
# pg_ldap_sync can run.
# Connection parameters to LDAP server
@ -25,6 +25,8 @@ ldap_users:
name_attribute: sAMAccountName
# lowercase name for use as PG role name
lowercase_name: true
# Add lowercase name *and* original name for use as PG role names (useful for migrating between case types)
bothcase_name: false
# Search parameters for LDAP groups which should be synchronized
ldap_groups:

View File

@ -23,6 +23,9 @@ mapping:
"uppercase_name":
type: bool
required: no
"bothcase_name":
type: bool
required: no
"ldap_groups":
type: map
@ -43,6 +46,9 @@ mapping:
"uppercase_name":
type: bool
required: no
"bothcase_name":
type: bool
required: no
"member_attribute":
type: str
required: yes

View File

@ -15,11 +15,11 @@ class Application
def string_to_symbol(hash)
if hash.kind_of?(Hash)
return hash.inject({}){|h, v|
return hash.inject({}) do |h, v|
raise "expected String instead of #{h.inspect}" unless v[0].kind_of?(String)
h[v[0].intern] = string_to_symbol(v[1])
h
}
end
else
return hash
end
@ -61,12 +61,22 @@ class Application
log.warn "user attribute #{ldap_user_conf[:name_attribute].inspect} not defined for #{entry.dn}"
next
end
name.downcase! if ldap_user_conf[:lowercase_name]
name.upcase! if ldap_user_conf[:uppercase_name]
log.info "found user-dn: #{entry.dn}"
user = LdapRole.new name, entry.dn
users << user
names = if ldap_user_conf[:bothcase_name]
[name, name.downcase].uniq
elsif ldap_user_conf[:lowercase_name]
[name.downcase]
elsif ldap_user_conf[:uppercase_name]
[name.upcase]
else
[name]
end
names.each do |n|
users << LdapRole.new(n, entry.dn)
end
entry.each do |attribute, values|
log.debug " #{attribute}:"
values.each do |value|
@ -89,12 +99,22 @@ class Application
log.warn "user attribute #{ldap_group_conf[:name_attribute].inspect} not defined for #{entry.dn}"
next
end
name.downcase! if ldap_group_conf[:lowercase_name]
name.upcase! if ldap_group_conf[:uppercase_name]
log.info "found group-dn: #{entry.dn}"
group = LdapRole.new name, entry.dn, entry[ldap_group_conf[:member_attribute]]
groups << group
names = if ldap_group_conf[:bothcase_name]
[name, name.downcase].uniq
elsif ldap_group_conf[:lowercase_name]
[name.downcase]
elsif ldap_group_conf[:uppercase_name]
[name.upcase]
else
[name]
end
names.each do |n|
groups << LdapRole.new(n, entry.dn, entry[ldap_group_conf[:member_attribute]])
end
entry.each do |attribute, values|
log.debug " #{attribute}:"
values.each do |value|
@ -108,8 +128,8 @@ class Application
PgRole = Struct.new :name, :member_names
# List of default roles taken from https://www.postgresql.org/docs/current/static/default-roles.html
PG_BUILTIN_ROLES = %w[ pg_signal_backend pg_monitor pg_read_all_settings pg_read_all_stats pg_stat_scan_tables]
# List of default roles taken from https://www.postgresql.org/docs/current/predefined-roles.html
PG_BUILTIN_ROLES = %w[ pg_read_all_data pg_write_all_data pg_read_all_settings pg_read_all_stats pg_stat_scan_tables pg_monitor pg_database_owner pg_signal_backend pg_read_server_files pg_write_server_files pg_execute_server_program ]
def search_pg_users
pg_users_conf = @config[:pg_users]
@ -185,12 +205,12 @@ class Application
r.type = type
end
log.info{
log.info do
roles.each do |role|
log.debug{ "#{role.state} #{role.type}: #{role.name}" }
end
"#{type} stat: create: #{roles.count{|r| r.state==:create }} drop: #{roles.count{|r| r.state==:drop }} keep: #{roles.count{|r| r.state==:keep }}"
}
end
return roles
end
@ -236,42 +256,42 @@ class Application
MatchedMembership = Struct.new :role_name, :has_member, :state
def match_memberships(ldap_roles, pg_roles)
ldap_by_dn = ldap_roles.inject({}){|h,r| h[r.dn] = r; h }
ldap_by_m2m = ldap_roles.inject([]){|a,r|
hash_of_arrays = Hash.new { |h, k| h[k] = [] }
ldap_by_dn = ldap_roles.inject(hash_of_arrays){|h,r| h[r.dn] << r; h }
ldap_by_m2m = ldap_roles.inject([]) do |a,r|
next a unless r.member_dns
a + r.member_dns.map{|dn|
if has_member=ldap_by_dn[dn]
a + r.member_dns.flat_map do |dn|
has_members = ldap_by_dn[dn]
log.warn{"ldap member with dn #{dn} is unknown"} if has_members.empty?
has_members.map do |has_member|
[r.name, has_member.name]
else
log.warn{"ldap member with dn #{dn} is unknown"}
nil
end
}.compact
}
end
end
pg_by_name = pg_roles.inject({}){|h,r| h[r.name] = r; h }
pg_by_m2m = pg_roles.inject([]){|a,r|
hash_of_arrays = Hash.new { |h, k| h[k] = [] }
pg_by_name = pg_roles.inject(hash_of_arrays){|h,r| h[r.name] << r; h }
pg_by_m2m = pg_roles.inject([]) do |a,r|
next a unless r.member_names
a + r.member_names.map{|name|
if has_member=pg_by_name[name]
a + r.member_names.flat_map do |name|
has_members = pg_by_name[name]
log.warn{"pg member with name #{name} is unknown"} if has_members.empty?
has_members.map do |has_member|
[r.name, has_member.name]
else
log.warn{"pg member with name #{name} is unknown"}
nil
end
}.compact
}
end
end
memberships = (ldap_by_m2m & pg_by_m2m).map{|r,mo| MatchedMembership.new r, mo, :keep }
memberships += (ldap_by_m2m - pg_by_m2m).map{|r,mo| MatchedMembership.new r, mo, :grant }
memberships += (pg_by_m2m - ldap_by_m2m).map{|r,mo| MatchedMembership.new r, mo, :revoke }
log.info{
log.info do
memberships.each do |membership|
log.debug{ "#{membership.state} #{membership.role_name} to #{membership.has_member}" }
end
"membership stat: grant: #{memberships.count{|u| u.state==:grant }} revoke: #{memberships.count{|u| u.state==:revoke }} keep: #{memberships.count{|u| u.state==:keep }}"
}
end
return memberships
end

View File

@ -1,3 +1,3 @@
module PgLdapSync
VERSION = "0.2.0"
VERSION = "0.3.0"
end

View File

@ -19,13 +19,14 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.rdoc_options = %w[--main README.md --charset=UTF-8]
spec.required_ruby_version = ">= 2.4"
spec.add_runtime_dependency "net-ldap", "~> 0.16"
spec.add_runtime_dependency "kwalify", "~> 0.7"
spec.add_runtime_dependency "pg", ">= 0.14", "< 2.0"
spec.add_development_dependency "ruby-ldapserver", "~> 0.3"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "bundler", ">= 1.16", "< 3.0"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "minitest-hooks", "~> 1.4"
end

View File

@ -0,0 +1,34 @@
---
ldap_connection:
host: localhost
port: 1389
ldap_users:
base: dc=example,dc=com
filter: (sAMAccountName=*)
name_attribute: sAMAccountName
bothcase_name: true
ldap_groups:
base: dc=example,dc=com
filter: (member=*)
name_attribute: cn
bothcase_name: true
member_attribute: member
pg_connection:
dbname: postgres
host: localhost
port: 54321
# needed for postgres-pr:
# user: insert_your_username_here
# password:
pg_users:
filter: rolcanlogin AND NOT rolsuper AND rolname!='double_user'
create_options: LOGIN
pg_groups:
filter: NOT rolcanlogin
create_options: NOLOGIN
grant_options:

View File

@ -11,14 +11,14 @@ cn=Fred Flintstone,dc=example,dc=com:
sn:
- Flintstone
sAMAccountName:
- fred
- Fred
cn=Wilma Flintstone,dc=example,dc=com:
cn:
- Wilma Flintstone
mail:
- wilma@bedrock.org
sAMAccountName:
- wilma
- Wilma
cn=Flintstones,dc=example,dc=com:
cn:
- Flintstones

View File

@ -83,7 +83,7 @@ class TestPgLdapSync < Minitest::Test
end
def setup
@pgconn.exec "DROP ROLE IF EXISTS fred, wilma, \"Flintstones\", \"Wilmas\", \"All Users\", double_user"
@pgconn.exec "DROP ROLE IF EXISTS \"Fred\", fred, \"Wilma\", wilma, \"Flintstones\", \"flintstones\", \"Wilmas\", \"wilmas\", \"All Users\", double_user"
end
def assert_role(role_name, attrs, member_of=[])
@ -130,12 +130,12 @@ class TestPgLdapSync < Minitest::Test
sync_with_config(config)
end
def sync_change
sync_to_fixture
def sync_change(fixture: "ldapdb", config: "config-ldapdb")
sync_to_fixture(fixture: fixture, config: config)
yield(@directory)
sync_with_config
sync_with_config(config)
exec_psql_du if $DEBUG
end
@ -153,8 +153,8 @@ class TestPgLdapSync < Minitest::Test
assert_role('All Users', 'Cannot login')
assert_role('Flintstones', 'Cannot login')
assert_role('Wilmas', 'Cannot login', ['All Users'])
assert_role('fred', '', ['All Users', 'Flintstones'])
assert_role('wilma', '', ['Flintstones', 'Wilmas'])
assert_role('Fred', '', ['All Users', 'Flintstones'])
assert_role('Wilma', '', ['Flintstones', 'Wilmas'])
end
def test_add_membership
@ -162,7 +162,17 @@ class TestPgLdapSync < Minitest::Test
# add 'Fred' to 'Wilmas'
@directory[0]['cn=Wilmas,dc=example,dc=com']['member'] << 'cn=Fred Flintstone,dc=example,dc=com'
end
assert_role('fred', '', ['All Users', 'Flintstones', 'Wilmas'])
refute_role('fred')
assert_role('Fred', '', ['All Users', 'Flintstones', 'Wilmas'])
end
def test_add_membership_bothcase
sync_change(config: "config-ldapdb-bothcase") do |dir|
# add 'Fred' to 'Wilmas'
@directory[0]['cn=Wilmas,dc=example,dc=com']['member'] << 'cn=Fred Flintstone,dc=example,dc=com'
end
assert_role('fred', '', ['All Users', 'all users', 'Flintstones', 'flintstones', 'Wilmas', 'wilmas'])
assert_role('Fred', '', ['All Users', 'all users', 'Flintstones', 'flintstones', 'Wilmas', 'wilmas'])
end
def test_revoke_membership
@ -170,7 +180,7 @@ class TestPgLdapSync < Minitest::Test
# revoke membership of 'wilma' to 'Flintstones'
dir[0]['cn=Flintstones,dc=example,dc=com']['member'].pop
end
assert_role('wilma', '', ['Wilmas'])
assert_role('Wilma', '', ['Wilmas'])
end
def test_rename_role
@ -179,6 +189,7 @@ class TestPgLdapSync < Minitest::Test
dir[0]['cn=Wilma Flintstone,dc=example,dc=com']['sAMAccountName'] = ['Wilma Flintstone']
end
refute_role('wilma')
refute_role('Wilma')
assert_role('Wilma Flintstone', '', ['Flintstones', 'Wilmas'])
end