Skip to content

Commit f95f985

Browse files
committed
Merge branch 'master' into local-activedirectory-integration-testing
2 parents ec9021e + b8407ed commit f95f985

File tree

13 files changed

+276
-49
lines changed

13 files changed

+276
-49
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# CHANGELOG
2+
3+
## v1.4.0
4+
5+
* Document constructor options [#57](https://github.com/github/github-ldap/pull/57)
6+
* [CI] Add Vagrant box for running tests against OpenLDAP locally [#55](https://github.com/github/github-ldap/pull/55)
7+
* Run all tests, including those in subdirectories [#54](https://github.com/github/github-ldap/pull/54)
8+
* Add ActiveDirectory membership validator [#52](https://github.com/github/github-ldap/pull/52)
9+
* Merge dev-v2 branch into master [#50](https://github.com/github/github-ldap/pull/50)
10+
* Pass through search options for GitHub::Ldap::Domain#user? [#51](https://github.com/github/github-ldap/pull/51)
11+
* Fix membership validation tests [#49](https://github.com/github/github-ldap/pull/49)
12+
* Add CI build for OpenLDAP integration [#48](https://github.com/github/github-ldap/pull/48)
13+
* Membership Validators [#45](https://github.com/github/github-ldap/pull/45)

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,15 @@ end
130130
3. Commit your changes (`git commit -am 'Add some feature'`)
131131
4. Push to the branch (`git push origin my-new-feature`)
132132
5. Create new Pull Request
133+
134+
## Releasing
135+
136+
This section is for gem maintainers to cut a new version of the gem. See
137+
[jch/release-scripts](https://github.com/jch/release-scripts) for original
138+
source of release scripts.
139+
140+
* Create a new branch from `master` named `release-x.y.z`, where `x.y.z` is the version to be released
141+
* Update `github-ldap.gemspec` to x.y.z following [semver](http://semver.org)
142+
* Run `script/changelog` and paste the draft into `CHANGELOG.md`. Edit as needed
143+
* Create pull request to solict feedback
144+
* After merging the pull request, on the master branch, run `script/release`

github-ldap.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Gem::Specification.new do |spec|
44
spec.name = "github-ldap"
5-
spec.version = "1.3.6"
5+
spec.version = "1.4.0"
66
spec.authors = ["David Calavera"]
77
spec.email = ["david.calavera@gmail.com"]
88
spec.description = %q{Ldap authentication for humans}

lib/github/ldap.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Ldap
3434
def_delegator :@connection, :open
3535

3636
attr_reader :uid, :search_domains, :virtual_attributes,
37+
:membership_validator,
3738
:instrumentation_service
3839

3940
# Build a new GitHub::Ldap instance
@@ -87,6 +88,9 @@ def initialize(options = {})
8788
# when a base is not explicitly provided.
8889
@search_domains = Array(options[:search_domains])
8990

91+
# configure which strategy should be used to validate user membership
92+
configure_membership_validation_strategy(options[:membership_validator])
93+
9094
# enables instrumenting queries
9195
@instrumentation_service = options[:instrumentation_service]
9296
end
@@ -182,6 +186,23 @@ def search(options, &block)
182186
end
183187
end
184188

189+
# Internal: Searches the host LDAP server's Root DSE for capabilities and
190+
# extensions.
191+
#
192+
# Returns a Net::LDAP::Entry object.
193+
def capabilities
194+
@capabilities ||=
195+
instrument "capabilities.github_ldap" do |payload|
196+
begin
197+
@connection.search_root_dse
198+
rescue Net::LDAP::LdapError => error
199+
payload[:error] = error
200+
# stubbed result
201+
Net::LDAP::Entry.new
202+
end
203+
end
204+
end
205+
185206
# Internal - Determine whether to use encryption or not.
186207
#
187208
# encryption: is the encryption method, either 'ssl', 'tls', 'simple_tls' or 'start_tls'.
@@ -214,5 +235,24 @@ def configure_virtual_attributes(attributes)
214235
VirtualAttributes.new(false)
215236
end
216237
end
238+
239+
# Internal: Configure the membership validation strategy.
240+
#
241+
# Used by GitHub::Ldap::MembershipValidators::Detect to force a specific
242+
# strategy (instead of detecting host capabilities and deciding at runtime).
243+
#
244+
# If `strategy` is not provided, or doesn't match a known strategy,
245+
# defaults to `:detect`. Otherwise the configured strategy is selected.
246+
#
247+
# Returns the selected membership validator strategy Symbol.
248+
def configure_membership_validation_strategy(strategy = nil)
249+
@membership_validator =
250+
case strategy.to_s
251+
when "classic", "recursive", "active_directory"
252+
strategy.to_sym
253+
else
254+
:detect
255+
end
256+
end
217257
end
218258
end

lib/github/ldap/membership_validators.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'github/ldap/membership_validators/base'
2+
require 'github/ldap/membership_validators/detect'
23
require 'github/ldap/membership_validators/classic'
34
require 'github/ldap/membership_validators/recursive'
45
require 'github/ldap/membership_validators/active_directory'
@@ -13,6 +14,13 @@ class Ldap
1314
# validator = GitHub::Ldap::MembershipValidators::Classic.new(ldap, groups)
1415
# validator.perform(entry) #=> true
1516
#
16-
module MembershipValidators; end
17+
module MembershipValidators
18+
# Internal: Mapping of strategy name to class.
19+
STRATEGIES = {
20+
:classic => GitHub::Ldap::MembershipValidators::Classic,
21+
:recursive => GitHub::Ldap::MembershipValidators::Recursive,
22+
:active_directory => GitHub::Ldap::MembershipValidators::ActiveDirectory
23+
}
24+
end
1725
end
1826
end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
module GitHub
2+
class Ldap
3+
module MembershipValidators
4+
# Detects the LDAP host's capabilities and determines the appropriate
5+
# membership validation strategy at runtime. Currently detects for
6+
# ActiveDirectory in-chain membership validation. An explicit strategy can
7+
# also be defined via `GitHub::Ldap#membership_validator=`. See also
8+
# `GitHub::Ldap#configure_membership_validation_strategy`.
9+
class Detect < Base
10+
# Internal: The capability required to use the ActiveDirectory strategy.
11+
# See: http://msdn.microsoft.com/en-us/library/cc223359.aspx.
12+
ACTIVE_DIRECTORY_V61_R2_OID = "1.2.840.113556.1.4.2080".freeze
13+
14+
def perform(entry)
15+
# short circuit validation if there are no groups to check against
16+
return true if groups.empty?
17+
18+
strategy.perform(entry)
19+
end
20+
21+
# Internal: Returns the membership validation strategy object.
22+
def strategy
23+
@strategy ||= begin
24+
strategy = detect_strategy
25+
strategy.new(ldap, groups)
26+
end
27+
end
28+
29+
# Internal: Detects LDAP host's capabilities and chooses the best
30+
# strategy for the host.
31+
#
32+
# If the strategy has been set explicitly, skips detection and uses the
33+
# configured strategy instead.
34+
#
35+
# Returns the strategy class.
36+
def detect_strategy
37+
case
38+
when GitHub::Ldap::MembershipValidators::STRATEGIES.key?(strategy_config)
39+
GitHub::Ldap::MembershipValidators::STRATEGIES[strategy_config]
40+
when active_directory_capability?
41+
GitHub::Ldap::MembershipValidators::STRATEGIES[:active_directory]
42+
else
43+
GitHub::Ldap::MembershipValidators::STRATEGIES[:recursive]
44+
end
45+
end
46+
47+
# Internal: Returns the configured membership validator strategy Symbol.
48+
def strategy_config
49+
ldap.membership_validator
50+
end
51+
52+
# Internal: Detect whether the LDAP host is an ActiveDirectory server.
53+
#
54+
# See: http://msdn.microsoft.com/en-us/library/cc223359.aspx.
55+
#
56+
# Returns true if the host is an ActiveDirectory server, false otherwise.
57+
def active_directory_capability?
58+
capabilities[:supportedcapabilities].include?(ACTIVE_DIRECTORY_V61_R2_OID)
59+
end
60+
61+
# Internal: Returns the Net::LDAP::Entry object describing the LDAP
62+
# host's capabilities (via the Root DSE).
63+
def capabilities
64+
ldap.capabilities
65+
end
66+
end
67+
end
68+
end
69+
end

script/changelog

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env sh
2+
# Usage: script/changelog [-r <repo>] [-b <base>] [-h <head>]
3+
#
4+
# repo: base string of GitHub repository url. e.g. "user_or_org/repository". Defaults to git remote url.
5+
# base: git ref to compare from. e.g. "v1.3.1". Defaults to latest git tag.
6+
# head: git ref to compare to. Defaults to "HEAD".
7+
#
8+
# Generate a changelog preview from pull requests merged between `base` and
9+
# `head`.
10+
#
11+
set -e
12+
13+
[ $# -eq 0 ] && set -- --help
14+
15+
# parse args
16+
repo=$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4-)
17+
base=$(git tag -l | sort -n | tail -n 1)
18+
head="HEAD"
19+
api_url="https://api.github.com"
20+
21+
echo "# $base..$head"
22+
echo
23+
24+
# get merged PR's. Better way is to query the API for these, but this is easier
25+
for pr in $(git log --oneline v1.3.6..HEAD | grep "Merge pull request" | awk '{gsub("#",""); print $5}')
26+
do
27+
# frustrated with trying to pull out the right values, fell back to ruby
28+
curl -s "$api_url/repos/$repo/pulls/$pr" | ruby -rjson -e 'pr=JSON.parse(STDIN.read); puts "* #{pr[%q(title)]} [##{pr[%q(number)]}](#{pr[%q(html_url)]})"'
29+
done

script/package

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
# Usage: script/package
3+
# Updates the gemspec and builds a new gem in the pkg directory.
4+
5+
mkdir -p pkg
6+
gem build *.gemspec
7+
mv *.gem pkg

script/release

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env bash
2+
# Usage: script/release
3+
# Build the package, tag a commit, push it to origin, and then release the
4+
# package publicly.
5+
6+
set -e
7+
8+
version="$(script/package | grep Version: | awk '{print $2}')"
9+
[ -n "$version" ] || exit 1
10+
11+
echo $version
12+
git commit --allow-empty -a -m "Release $version"
13+
git tag "v$version"
14+
git push origin
15+
git push origin "v$version"
16+
gem push pkg/*-${version}.gem

test/ldap_test.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,34 @@ def test_instruments_search
7272
assert_equal "(uid=user1)", payload[:filter].to_s
7373
assert_equal "dc=github,dc=com", payload[:base]
7474
end
75+
76+
def test_membership_validator_default
77+
assert_equal :detect, @ldap.membership_validator
78+
end
79+
80+
def test_membership_validator_configured_to_classic_strategy
81+
@ldap.configure_membership_validation_strategy :classic
82+
assert_equal :classic, @ldap.membership_validator
83+
end
84+
85+
def test_membership_validator_configured_to_recursive_strategy
86+
@ldap.configure_membership_validation_strategy :recursive
87+
assert_equal :recursive, @ldap.membership_validator
88+
end
89+
90+
def test_membership_validator_configured_to_active_directory_strategy
91+
@ldap.configure_membership_validation_strategy :active_directory
92+
assert_equal :active_directory, @ldap.membership_validator
93+
end
94+
95+
def test_membership_validator_misconfigured_to_unrecognized_strategy_falls_back_to_default
96+
@ldap.configure_membership_validation_strategy :unknown
97+
assert_equal :detect, @ldap.membership_validator
98+
end
99+
100+
def test_capabilities
101+
assert_kind_of Net::LDAP::Entry, @ldap.capabilities
102+
end
75103
end
76104

77105
class GitHubLdapTest < GitHub::Ldap::Test

0 commit comments

Comments
 (0)