diff --git a/.junie/guidelines.md b/.junie/guidelines.md
index a104883..94a8baf 100644
--- a/.junie/guidelines.md
+++ b/.junie/guidelines.md
@@ -12,7 +12,7 @@ This document captures project-specific knowledge to streamline setup, testing,
- See .env.local.example for an example of what to put in .env.local.
- See CONTRIBUTING.md for details on how to set up your local environment.
- Ruby and Bundler
- - Runtime supports Ruby >= 0
+ - Runtime supports Ruby >= 2.0
- Development tooling targets Ruby >= 2.3 (minimum supported by setup-ruby GHA).
- Use a recent Ruby (>= 3.4 recommended) for fastest setup and to exercise modern coverage behavior.
- Install dependencies via Bundler in project root:
diff --git a/.opencollective.yml b/.opencollective.yml
deleted file mode 100644
index 1eaf11d..0000000
--- a/.opencollective.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-collective: "omniauth"
-readme-backers-commit-subject: "💸 Thanks 🙏 to our new backers 🎒 and subscribers 📜"
-readme-osc-tag: "OPENCOLLECTIVE"
diff --git a/README.md b/README.md
index e0b4154..e5fc4fe 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
| 📍 NOTE |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| RubyGems (the [GitHub org][rubygems-org], not the website) [suffered][draper-security] a [hostile takeover][ellen-takeover] in September 2025. |
+| Ultimately [4 maintainers][simi-removed] were [hard removed][martin-removed] and a reason has been given for only 1 of those, while 2 others resigned in protest. |
| It is a [complicated story][draper-takeover] which is difficult to [parse quickly][draper-lies]. |
| I'm adding notes like this to gems because I [don't condone theft][draper-theft] of repositories or gems from their rightful owners. |
| If a similar theft happened with my repos/gems, I'd hope some would stand up for me. |
@@ -37,6 +38,7 @@
# 📁 OmniAuth LDAP
+[![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
@@ -75,10 +77,9 @@ All of the listed options are required, with the exception of `:title`, `:name_p
| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i]
[![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
| Works with MRI Ruby 2 | ![Ruby 2.0 Compat][💎ruby-2.0i] ![Ruby 2.1 Compat][💎ruby-2.1i] ![Ruby 2.2 Compat][💎ruby-2.2i]
[![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] |
-| Works with MRI Ruby 1 | ![Ruby 1.8 Compat][💎ruby-1.8i] ![Ruby 1.9 Compat][💎ruby-1.9i] |
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
-| Source | [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
-| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
+| Source | [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
+| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
@@ -92,7 +93,7 @@ Compatible with MRI Ruby 2.0+, and concordant releases of JRuby, and TruffleRuby
|------------------------------------------------|--------------------------------------------------------|
| 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
-### Ruby 3.4
+#### Ruby 3.4
nkf/kconv has been part of Ruby since long ago.
Eventually it became a standard gem, but was changed to a bundled gem in Ruby 3.4.
@@ -547,7 +548,6 @@ Thanks for RTFM. ☺️
[🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
[💁🏼♂️peterboling]: http://www.peterboling.com
[🚂railsbling]: http://www.railsbling.com
-[📜src-cb]: https://codeberg.org/omniauth/omniauth-ldap
[📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green
[📜src-gh]: https://github.com/omniauth/omniauth-ldap
[📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
@@ -600,8 +600,6 @@ Thanks for RTFM. ☺️
[🚎14-🔓️-wfi]: https://github.com/omniauth/omniauth-ldap/actions/workflows/unlocked_deps.yml/badge.svg
[🚎15-🪪-wf]: https://github.com/omniauth/omniauth-ldap/actions/workflows/license-eye.yml
[🚎15-🪪-wfi]: https://github.com/omniauth/omniauth-ldap/actions/workflows/license-eye.yml/badge.svg
-[💎ruby-1.8i]: https://img.shields.io/badge/Ruby-1.8_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
-[💎ruby-1.9i]: https://img.shields.io/badge/Ruby-1.9_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
[💎ruby-2.0i]: https://img.shields.io/badge/Ruby-2.0_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
[💎ruby-2.1i]: https://img.shields.io/badge/Ruby-2.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
[💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
@@ -645,7 +643,7 @@ Thanks for RTFM. ☺️
[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
[📌gitmoji]:https://gitmoji.dev
-[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
+[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
[🧮kloc-img]: https://img.shields.io/badge/KLOC-4.076-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
[🔐security]: SECURITY.md
diff --git a/omniauth-ldap.gemspec b/omniauth-ldap.gemspec
index 6ffba59..583fb1e 100644
--- a/omniauth-ldap.gemspec
+++ b/omniauth-ldap.gemspec
@@ -115,7 +115,7 @@ Gem::Specification.new do |spec|
# visibility and discoverability.
# However, development dependencies in gemspec will install on
# all versions of Ruby that will run in CI.
- # This gem, and its gemspec runtime dependencies, will install on Ruby down to 0.
+ # This gem, and its gemspec runtime dependencies, will install on Ruby down to 2.0.
# This gem, and its gemspec development dependencies, will install on Ruby down to 2.3.
# Thus, dev dependencies in gemspec must have
#
diff --git a/sig/omniauth-ldap.rbs b/sig/omniauth-ldap.rbs
new file mode 100644
index 0000000..399b294
--- /dev/null
+++ b/sig/omniauth-ldap.rbs
@@ -0,0 +1,5 @@
+# Top-level signature file for the gem. Detailed signatures live in the files below:
+# - sig/omniauth/ldap/version.rbs
+# - sig/omniauth/ldap/adaptor.rbs
+# - sig/omniauth/strategies/ldap.rbs
+# This file is intentionally minimal to avoid duplicating declarations.
diff --git a/sig/omniauth/ldap/adaptor.rbs b/sig/omniauth/ldap/adaptor.rbs
new file mode 100644
index 0000000..accee0e
--- /dev/null
+++ b/sig/omniauth/ldap/adaptor.rbs
@@ -0,0 +1,54 @@
+module OmniAuth
+ module LDAP
+ class Adaptor
+ class LdapError < ::StandardError
+ end
+
+ class ConfigurationError < ::StandardError
+ end
+
+ class AuthenticationError < ::StandardError
+ end
+
+ class ConnectionError < ::StandardError
+ end
+
+ VALID_ADAPTER_CONFIGURATION_KEYS: Array[Symbol]
+ MUST_HAVE_KEYS: Array[untyped]
+ METHOD: Hash[Symbol, Symbol?]
+
+ attr_accessor bind_dn: String?
+ attr_accessor password: String?
+
+ # Net::LDAP is provided by the net-ldap gem; we reference it here for clarity.
+ attr_reader connection: Net::LDAP
+ attr_reader uid: String?
+ attr_reader base: String?
+ # auth is the hash passed to Net::LDAP#auth or similar
+ attr_reader auth: Hash[Symbol, untyped]
+ # filter is an LDAP filter string when configured
+ attr_reader filter: String?
+
+ # Validate that required keys exist in the configuration
+ def self.validate: (?Hash[Symbol, untyped]) -> void
+ def initialize: (?Hash[Symbol, untyped]) -> void
+
+ # Perform a search and optionally bind; returns the matched entry or false
+ def bind_as: (?Hash[Symbol, untyped]) -> (Net::LDAP::Entry? | false)
+
+ private
+
+ # Returns a Net::LDAP encryption symbol (e.g. :simple_tls, :start_tls) or nil
+ def ensure_method: (untyped) -> Symbol?
+
+ # Returns an array of SASL auth hashes
+ def sasl_auths: (?Hash[Symbol, untyped]) -> Array[Hash[Symbol, untyped]]
+
+ # Returns initial credential (string) and a proc that accepts a challenge and returns the response
+ # Use Array[untyped] here to avoid tuple syntax issues in some linters; the runtime value
+ # is commonly a two-element array [initial_credential, proc].
+ def sasl_bind_setup_digest_md5: (?Hash[Symbol, untyped]) -> Array[untyped]
+ def sasl_bind_setup_gss_spnego: (?Hash[Symbol, untyped]) -> Array[untyped]
+ end
+ end
+end
diff --git a/sig/omniauth/ldap/version.rbs b/sig/omniauth/ldap/version.rbs
new file mode 100644
index 0000000..69199f2
--- /dev/null
+++ b/sig/omniauth/ldap/version.rbs
@@ -0,0 +1,11 @@
+module OmniAuth
+ module LDAP
+ module Version
+ VERSION: String
+ end
+
+ # Traditional constant alias
+ VERSION: String
+ end
+end
+
diff --git a/sig/omniauth/strategies/ldap.rbs b/sig/omniauth/strategies/ldap.rbs
new file mode 100644
index 0000000..04a629c
--- /dev/null
+++ b/sig/omniauth/strategies/ldap.rbs
@@ -0,0 +1,24 @@
+module OmniAuth
+ module Strategies
+ class LDAP
+ OMNIAUTH_GTE_V2: bool
+
+ # CONFIG is a read-only mapping of string keys to mapping definitions
+ CONFIG: Hash[String, untyped]
+
+ # The request_phase either returns a Rack-compatible response or the form response.
+ def request_phase: () -> (Rack::Response | Array[untyped] | String)
+
+ # The callback_phase may call super (untyped) or return a failure symbol
+ def callback_phase: () -> untyped
+
+ # Accepts an adaptor and returns a Net::LDAP::Filter or similar
+ def filter: (OmniAuth::LDAP::Adaptor) -> Net::LDAP::Filter
+
+ # Map a user object (Net::LDAP::Entry-like) into a Hash for the auth info
+ def self.map_user: (Hash[String, untyped], untyped) -> Hash[String, untyped]
+
+ def missing_credentials?: () -> bool
+ end
+ end
+end
diff --git a/sig/rbs/net-ldap.rbs b/sig/rbs/net-ldap.rbs
new file mode 100644
index 0000000..edc8f02
--- /dev/null
+++ b/sig/rbs/net-ldap.rbs
@@ -0,0 +1,19 @@
+# Minimal stubs for net-ldap types used by the gem
+module Net
+ class LDAP
+ def initialize: (Hash[Symbol, untyped]) -> void
+ def open: () { (self) -> untyped } -> untyped
+ def search: (?Hash[Symbol, untyped]) -> Array[Net::LDAP::Entry]
+ def bind: (?Hash[Symbol, untyped]) -> bool
+ end
+
+ class LDAP::Entry
+ def dn: () -> String
+ end
+
+ class LDAP::Filter
+ def self.construct: (String) -> Net::LDAP::Filter
+ def self.eq: (String, String) -> Net::LDAP::Filter
+ end
+end
+
diff --git a/sig/rbs/net-ntlm.rbs b/sig/rbs/net-ntlm.rbs
new file mode 100644
index 0000000..a6e898f
--- /dev/null
+++ b/sig/rbs/net-ntlm.rbs
@@ -0,0 +1,16 @@
+# Minimal stubs for net-ntlm types used by the gem
+module Net
+ module NTLM
+ class Message
+ def self.parse: (untyped) -> Net::NTLM::Message
+ def response: (?Hash[Symbol, untyped], ?Hash[Symbol, untyped]) -> Net::NTLM::Message
+ end
+
+ class Message::Type1
+ def serialize: () -> String
+ end
+
+ def self.encode_utf16le: (String) -> String
+ end
+end
+
diff --git a/sig/rbs/sasl.rbs b/sig/rbs/sasl.rbs
new file mode 100644
index 0000000..7894116
--- /dev/null
+++ b/sig/rbs/sasl.rbs
@@ -0,0 +1,12 @@
+# Minimal stubs for SASL bindings used in tests
+module SASL
+ class Preferences
+ def initialize: (?Hash[Symbol, untyped]) -> void
+ end
+
+ class SASL
+ def initialize: (String, SASL::Preferences) -> void
+ def receive: (String, untyped) -> [untyped, untyped]
+ end
+end
+