diff --git a/.envrc b/.envrc
index ec029d8..b56b211 100644
--- a/.envrc
+++ b/.envrc
@@ -27,6 +27,7 @@ export MAX_ROWS=1 # Setting for simplecov-console gem for tty output, limits to
# Internal Debugging Controls
export DEBUG=false # do not allow byebug statements (override in .env.local)
+export REQUIRE_BENCH=false # set to true in .env.local to turn on require_bench
# .env would override anything in this file, if `dotenv` is uncommented below.
# .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments,
diff --git a/.simplecov b/.simplecov
index bfe90c0..b540b12 100644
--- a/.simplecov
+++ b/.simplecov
@@ -1,3 +1,5 @@
require "kettle/soup/cover/config"
-SimpleCov.start
+SimpleCov.start do
+ add_filter "test/**/*"
+end
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8dc6e04..e23e6f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,9 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
### Removed
-## 2.0.2 - 2024-09-20
+## 2.0.2 - 2024-09-25
+- COVERAGE: 97.72% -- 214/219 lines in 4 files
+- BRANCH COVERAGE: 86.00% -- 43/50 branches in 4 files
+- 39.13% documented
### Added
- Improved integration with direnv for development
+- Better test coverage
+- More tests
+- rots v1.0.0 for tests
+### Changed
+- Upgraded to ruby-openid2 v3.1.0
+- Moved to oauth-xx organization
## 2.0.1 - 2024-09-05
### Added
diff --git a/Gemfile b/Gemfile
index e4a17b2..a2b5f72 100644
--- a/Gemfile
+++ b/Gemfile
@@ -14,5 +14,3 @@ platform :mri do
# Debugging
gem "byebug", ">= 11"
end
-
-gem "rots", github: "oauth-xx/rots"
diff --git a/Gemfile.lock b/Gemfile.lock
index a2ccd6c..10ef762 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,19 +1,9 @@
-GIT
- remote: https://github.com/oauth-xx/rots
- revision: caeacbf72b3b5966c3a299391911557d5771ceee
- specs:
- rots (0.2.2)
- net-http
- rack (>= 2.0)
- ruby-openid2 (~> 3.0)
- yaml
-
PATH
remote: .
specs:
- rack-openid2 (2.0.1)
+ rack-openid2 (2.0.2)
rack (>= 2.2)
- ruby-openid2 (~> 3.0, >= 3.0.1)
+ ruby-openid2 (~> 3.1, >= 3.1.0)
version_gem (~> 1.1, >= 1.1.4)
GEM
@@ -23,6 +13,7 @@ GEM
ast (2.4.2)
backports (3.25.0)
byebug (11.1.3)
+ date (3.3.4)
diff-lcs (1.5.1)
diffy (3.4.2)
docile (1.4.1)
@@ -38,32 +29,54 @@ GEM
version_gem (~> 1.1, >= 1.1.4)
language_server-protocol (3.17.0.3)
lint_roller (1.1.0)
+ logger (1.6.1)
minitest (5.25.1)
+ minitest-focus (1.4.0)
+ minitest (>= 4, < 6)
minitest-rg (5.3.0)
minitest (~> 5.0)
- net-http (0.4.1)
- uri
+ openssl (3.2.0)
+ optparse (0.5.0)
ostruct (0.6.0)
parallel (1.26.3)
parser (3.3.5.0)
ast (~> 2.4.1)
racc
+ psych (5.1.2)
+ stringio
racc (1.8.1)
rack (3.1.7)
rack-session (2.0.0)
rack (>= 3.0.0)
+ rackup (2.1.0)
+ rack (>= 3)
+ webrick (~> 1.8)
rainbow (3.1.1)
rake (13.2.1)
regexp_parser (2.9.2)
+ require_bench (1.0.4)
+ version_gem (>= 1.1.3, < 4)
rexml (3.3.7)
+ rots (1.0.0)
+ date
+ openssl
+ optparse
+ psych (~> 5.1)
+ rack (>= 2)
+ rackup (>= 2)
+ ruby-openid2 (~> 3.1, >= 3.1.0)
+ stringio
+ version_gem (~> 1.1, >= 1.1.4)
+ webrick
+ yaml (~> 0.3)
rspec-block_is_expected (1.0.6)
- rubocop (1.64.1)
+ rubocop (1.65.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 1.8, < 3.0)
+ regexp_parser (>= 2.4, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
@@ -104,9 +117,7 @@ GEM
rubocop (~> 1.51)
rubocop-thread_safety (0.5.1)
rubocop (>= 0.90.0)
- ruby-openid2 (3.0.1)
- net-http (~> 0.4, >= 0.4.1)
- rexml (~> 3.3, >= 3.3.7)
+ ruby-openid2 (3.1.0)
version_gem (~> 1.1, >= 1.1.4)
ruby-progressbar (1.13.0)
simplecov (0.22.0)
@@ -125,10 +136,10 @@ GEM
simplecov-rcov (0.3.7)
simplecov (>= 0.4.1)
simplecov_json_formatter (0.1.4)
- standard (1.37.0)
+ standard (1.40.0)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0)
- rubocop (~> 1.64.0)
+ rubocop (~> 1.65.0)
standard-custom (~> 1.0.0)
standard-performance (~> 1.4)
standard-custom (1.0.2)
@@ -143,11 +154,12 @@ GEM
standard-custom (>= 1.0.2, < 2)
standard-performance (>= 1.3.1, < 2)
version_gem (>= 1.1.4, < 3)
+ stringio (3.1.1)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.6.0)
- uri (0.13.1)
version_gem (1.1.4)
+ webrick (1.8.2)
yaml (0.3.0)
yard (0.9.37)
yard-junk (0.0.10)
@@ -162,12 +174,15 @@ PLATFORMS
DEPENDENCIES
byebug (>= 11)
kettle-soup-cover (~> 1.0, >= 1.0.2)
- minitest (>= 5)
+ logger (~> 1.6, >= 1.6.1)
+ minitest (>= 5, < 6)
+ minitest-focus (~> 1.4)
minitest-rg (>= 5)
rack-openid2!
rack-session (>= 2)
rake (>= 13)
- rots!
+ require_bench (~> 1.0, >= 1.0.4)
+ rots (~> 1.0)
rubocop-lts (~> 18.2, >= 18.2.1)
rubocop-minitest (~> 0.36)
rubocop-packaging (~> 0.5, >= 0.5.2)
diff --git a/README.md b/README.md
index 7bc0ec8..81f2e4c 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
+[![Version](https://img.shields.io/gem/v/rack-openid2.svg)](https://rubygems.org/gems/rack-openid2)
+[![Downloads Today](https://img.shields.io/gem/rd/rack-openid2.svg)](https://github.com/oauth-xx/rack-openid2)
[![CI Supported Build][🚎s-wfi]][🚎s-wf]
[![CI Unsupported Build][🚎us-wfi]][🚎us-wf]
[![CI Style Build][🚎st-wfi]][🚎st-wf]
@@ -19,10 +21,46 @@
[🚎hd-wf]: https://github.com/oauth-xx/rack-openid2/actions/workflows/heads.yml
[🚎hd-wfi]: https://github.com/oauth-xx/rack-openid2/actions/workflows/heads.yml/badge.svg
+-----
+
+
+
+[![Liberapay Patrons][⛳liberapay-img]][⛳liberapay]
+[![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor]
+[![Polar Shield][🖇polar-img]][🖇polar]
+[![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi]
+[![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
+
+[⛳liberapay-img]: https://img.shields.io/liberapay/patrons/pboling.svg?logo=liberapay
+[⛳liberapay]: https://liberapay.com/pboling/donate
+[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
+[🖇sponsor]: https://github.com/sponsors/pboling
+[🖇polar-img]: https://polar.sh/embed/seeks-funding-shield.svg?org=pboling
+[🖇polar]: https://polar.sh/pboling
+[🖇kofi-img]: https://img.shields.io/badge/buy%20me%20coffee-donate-yellow.svg
+[🖇kofi]: https://ko-fi.com/O5O86SNP4
+[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-yellow.svg
+[🖇patreon]: https://patreon.com/galtzo
+
+
+
+
+
+
Provides a more HTTPish API around the ruby-openid library.
+## Installation
+
+Install the gem and add to the application's Gemfile by executing:
+
+ $ bundle add rack-openid2
+
+If bundler is not being used to manage dependencies, install the gem by executing:
+
+ $ gem install rack-openid2
+
## Usage
You trigger an OpenID request similar to HTTP authentication. From your app, return a "401 Unauthorized" and a "WWW-Authenticate" header with the identifier you would like to validate.
@@ -94,6 +132,36 @@ __END__
```
+## General Info
+
+| Primary Namespace | `Rack::OpenID` |
+|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| gem name | [ruby-openid2](https://rubygems.org/gems/rack-openid2) |
+| code triage | [![Open Source Helpers](https://www.codetriage.com/oauth-xx/rack-openid2/badges/users.svg)](https://www.codetriage.com/oauth-xx/rack-openid2) |
+| documentation | [on Github.com][homepage], [on Rdoc.info][documentation] |
+| expert support | [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github) |
+| `...` 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Follow Me on LinkedIn][🖇linkedin-img]][🖇linkedin] [![Find Me on WellFound:][✌️wellfound-img]][✌️wellfound] [![Find Me on CrunchBase][💲crunchbase-img]][💲crunchbase] [![My LinkTree][🌳linktree-img]][🌳linktree] [![Follow Me on Ruby.Social][🐘ruby-mast-img]][🐘ruby-mast] [![Tweet @ Peter][🐦tweet-img]][🐦tweet] [💻][coderme] [🌏][aboutme] |
+
+
+[🐦tweet-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow%20%40galtzo
+[🐦tweet]: http://twitter.com/galtzo
+[🚎blog]: http://www.railsbling.com/tags/rack-openid2/
+[🚎blog-img]: https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat
+[🖇linkedin]: http://www.linkedin.com/in/peterboling
+[🖇linkedin-img]: https://img.shields.io/badge/PeterBoling-blue?style=plastic&logo=linkedin
+[✌️wellfound]: https://angel.co/u/peter-boling
+[✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=plastic&logo=wellfound
+[💲crunchbase]: https://www.crunchbase.com/person/peter-boling
+[💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=plastic&logo=crunchbase
+[🐘ruby-mast]: https://ruby.social/@galtzo
+[🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https%3A%2F%2Fruby.social&style=plastic&logo=mastodon&label=Ruby%20%40galtzo
+[🌳linktree]: https://linktr.ee/galtzo
+[🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=plastic&logo=linktree
+
+
+[aboutme]: https://about.me/peter.boling
+[coderme]: https://coderwall.com/Peter%20Boling
+
## TODO
- 1 failing test (skipped)
diff --git a/Rakefile b/Rakefile
index fc0e04d..3bb2d6f 100644
--- a/Rakefile
+++ b/Rakefile
@@ -2,11 +2,10 @@ require "bundler/gem_tasks"
require "rake/testtask"
+require "require_bench" if ENV.fetch("REQUIRE_BENCH").casecmp?("true")
+
desc "Run tests"
Rake::TestTask.new("test") do |t|
- t.libs << "lib"
- t.libs << "test"
- t.test_files = FileList["test/**/test_*.rb"]
t.verbose = false
end
@@ -40,4 +39,4 @@ rescue LoadError
end
end
-task default: %i[test rubocop_gradual yard yard:junk]
+task default: %i[test rubocop_gradual:autocorrect yard yard:junk]
diff --git a/lib/rack/openid/version.rb b/lib/rack/openid/version.rb
index c8225fa..f6d2c07 100644
--- a/lib/rack/openid/version.rb
+++ b/lib/rack/openid/version.rb
@@ -1,7 +1,7 @@
module Rack
class OpenID
module Version
- VERSION = "2.0.1"
+ VERSION = "2.0.2"
end
end
end
diff --git a/rack-openid2.gemspec b/rack-openid2.gemspec
index 6522efc..c5749ee 100644
--- a/rack-openid2.gemspec
+++ b/rack-openid2.gemspec
@@ -42,18 +42,26 @@ Gem::Specification.new do |spec|
spec.metadata["rubygems_mfa_required"] = "true"
spec.add_dependency("rack", ">= 2.2")
- spec.add_dependency("ruby-openid2", "~> 3.0", ">= 3.0.1")
+ spec.add_dependency("ruby-openid2", "~> 3.1", ">= 3.1.0")
spec.add_dependency("version_gem", "~> 1.1", ">= 1.1.4")
# Testing
- spec.add_development_dependency("minitest", ">= 5")
+ spec.add_development_dependency("minitest", ">= 5", "< 6") # Use assert_nil if expecting nil
+ spec.add_development_dependency("minitest-focus", "~> 1.4")
spec.add_development_dependency("minitest-rg", ">= 5")
spec.add_development_dependency("rack-session", ">= 2")
spec.add_development_dependency("rake", ">= 13")
+ spec.add_development_dependency("rots", "~> 1.0")
+
+ # Test Logging
+ spec.add_development_dependency("logger", "~> 1.6", ">= 1.6.1")
# Coverage
spec.add_development_dependency("kettle-soup-cover", "~> 1.0", ">= 1.0.2")
+ # Debugging
+ spec.add_development_dependency("require_bench", "~> 1.0", ">= 1.0.4")
+
# Linting
spec.add_development_dependency("rubocop-lts", "~> 18.2", ">= 18.2.1")
spec.add_development_dependency("rubocop-minitest", "~> 0.36")
diff --git a/test/test_helper.rb b/test/support/config.rb
similarity index 56%
rename from test/test_helper.rb
rename to test/support/config.rb
index 7807194..14f5ac9 100644
--- a/test/test_helper.rb
+++ b/test/support/config.rb
@@ -1,14 +1,15 @@
# External dependencies
+require "require_bench" if ENV.fetch("REQUIRE_BENCH").casecmp?("true")
require "byebug" if ENV.fetch("DEBUG", "false").casecmp?("true")
require "net/http"
require "rack"
require "rack/session"
-# testing libraries
+# External testing libraries
require "minitest/rg"
# Test support
-require "support/logging"
+require_relative "logging"
## Last thing before loading this gem is to setup code coverage
begin
@@ -22,9 +23,18 @@
# Testing libraries that need to load after simplecov
require "minitest/autorun"
+require "minitest/focus"
-# Internal dependencies & mixins
-require "rack/openid"
-require "rack/openid/simple_auth"
+# rots depends on this library, but the tests here also depend on it,
+# so it needs to load after simplecov, in order to get accurate coverage of this gem,
+# since this gem is loaded by rots.
+require "rots"
+require "rots/mocks"
+require "rots/test"
OpenID::Util.logger = TestLogging::LOGGER
+OpenID.fetcher = Rots::Mocks::Fetcher.new(Rots::Mocks::RotsServer.new)
+
+# This library
+require "rack-openid2"
+require "rack/openid/simple_auth"
diff --git a/test/test_integration.rb b/test/test_integration.rb
index b36b71f..128a2a6 100644
--- a/test/test_integration.rb
+++ b/test/test_integration.rb
@@ -1,441 +1,259 @@
-# External libraries
-require "rots"
+require_relative "support/config"
-require_relative "test_helper"
+describe "openid integration" do
+ include Rots::Test::RackTestHelpers
-describe "integration" do
- class MockFetcher
- def initialize(app)
- @app = app
- end
-
- def fetch(url, body = nil, headers = nil, limit = nil)
- opts = (headers || {}).dup
- opts[:input] = body
- opts[:method] = "POST" if body
- env = Rack::MockRequest.env_for(url, opts)
-
- status, headers, body = @app.call(env)
-
- buf = []
- buf << "HTTP/1.1 #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
- headers.each { |header, value| buf << "#{header}: #{value}" }
- buf << ""
- body.each { |part| buf << part }
+ it "with_get" do
+ app = app({})
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
- io = Net::BufferedIO.new(StringIO.new(buf.join("\n")))
- res = Net::HTTPResponse.read_new(io)
- res.reading_body(io, true) {}
- OpenID::HTTPResponse._from_net_response(res, url)
- end
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
end
- ROTS_SERVER_URL = "http://localhost:9292"
-
- RotsApp = Rack::Builder.new do
- config = {
- "identity" => "john.doe",
- "sreg" => {
- "nickname" => "jdoe",
- "fullname" => "John Doe",
- "email" => "jhon@doe.com",
- "dob" => Date.parse("1985-09-21"),
- "gender" => "M",
- },
- }
-
- map("/%s" % config["identity"]) do
- run(Rots::IdentityPageApp.new(config, {}))
- end
+ it "with_deprecated_identity" do
+ app = app({})
+ mock_openid_request(app, "/", method: "GET", identity: "#{Rots::Mocks::RotsServer::SERVER_URL}/john.doe?openid.success=true")
+ follow_openid_redirect!(app)
- map("/server") do
- run(Rots::ServerApp.new(config, storage: Dir.tmpdir))
- end
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
end
- OpenID.fetcher = MockFetcher.new(RotsApp)
+ it "with_post_method" do
+ app = app({})
+ mock_openid_request(app, "/", method: "POST")
+ follow_openid_redirect!(app)
- module RackTestHelpers
- private
-
- def process(*args)
- env = Rack::MockRequest.env_for(*args)
- @response = Rack::MockResponse.new(*@app.call(env))
- end
-
- def follow_redirect!
- assert(@response)
- assert_equal(303, @response.status)
-
- env = Rack::MockRequest.env_for(@response.headers["Location"])
- _status, headers, _body = RotsApp.call(env)
-
- uri = URI(headers["Location"])
- process("#{uri.path}?#{uri.query}")
- end
+ assert_equal 200, @response.status
+ assert_equal "POST", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
end
- describe "headers" do
- it "builds header" do
- assert_equal 'OpenID identity="http://example.com/"',
- Rack::OpenID.build_header(identity: "http://example.com/")
- assert_equal 'OpenID identity="http://example.com/?foo=bar"',
- Rack::OpenID.build_header(identity: "http://example.com/?foo=bar")
+ it "with_custom_return_to" do
+ app = app(return_to: "http://example.org/complete")
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
- header = Rack::OpenID.build_header(identity: "http://example.com/", return_to: "http://example.org/")
-
- assert_match(/OpenID /, header)
- assert_match(/identity="http:\/\/example\.com\/"/, header)
- assert_match(/return_to="http:\/\/example\.org\/"/, header)
-
- header = Rack::OpenID.build_header(identity: "http://example.com/", required: ["nickname", "email"])
-
- assert_match(/OpenID /, header)
- assert_match(/identity="http:\/\/example\.com\/"/, header)
- assert_match(/required="nickname,email"/, header)
- end
-
- it "parses header" do
- assert_equal(
- {"identity" => "http://example.com/"},
- Rack::OpenID.parse_header('OpenID identity="http://example.com/"'),
- )
- assert_equal(
- {"identity" => "http://example.com/?foo=bar"},
- Rack::OpenID.parse_header('OpenID identity="http://example.com/?foo=bar"'),
- )
- assert_equal(
- {"identity" => "http://example.com/", "return_to" => "http://example.org/"},
- Rack::OpenID.parse_header('OpenID identity="http://example.com/", return_to="http://example.org/"'),
- )
- assert_equal(
- {"identity" => "http://example.com/", "required" => ["nickname", "email"]},
- Rack::OpenID.parse_header('OpenID identity="http://example.com/", required="nickname,email"'),
- )
-
- # ensure we don't break standard HTTP basic auth
- assert_empty(
- Rack::OpenID.parse_header('Realm="Example"'),
- )
- end
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/complete", @response.headers["X-Path"]
+ assert_equal "success", @response.body
end
- describe "openid" do
- include RackTestHelpers
-
- it "with_get" do
- @app = app({})
- process("/", method: "GET")
- follow_redirect!
-
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
-
- it "with_deprecated_identity" do
- @app = app({})
- process("/", method: "GET", identity: "#{ROTS_SERVER_URL}/john.doe?openid.success=true")
- follow_redirect!
-
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ it "with_get_nested_params_custom_return_to" do
+ url = "http://example.org/complete?user[remember_me]=true"
+ app = app(return_to: url)
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
+
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/complete", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ assert_match(/remember_me/, @response.headers["X-Query-String"])
+ end
- it "with_post_method" do
- @app = app({})
- process("/", method: "POST")
- follow_redirect!
+ it "with_post_nested_params_custom_return_to" do
+ url = "http://example.org/complete?user[remember_me]=true"
+ app = app(return_to: url)
+ mock_openid_request(app, "/", method: "POST")
- assert_equal 200, @response.status
- assert_equal "POST", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ assert_equal 303, @response.status
+ env = Rack::MockRequest.env_for(@response.headers["Location"])
+ _status, headers, _body = Rots::Mocks::RotsServer.new.call(env)
- it "with_custom_return_to" do
- @app = app(return_to: "http://example.org/complete")
- process("/", method: "GET")
- follow_redirect!
+ _uri, input = headers["Location"].split("?", 2)
+ mock_openid_request(app, "http://example.org/complete?user[remember_me]=true", method: "POST", input: input)
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/complete", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
-
- it "with_get_nested_params_custom_return_to" do
- url = "http://example.org/complete?user[remember_me]=true"
- @app = app(return_to: url)
- process("/", method: "GET")
- follow_redirect!
+ assert_equal 200, @response.status
+ assert_equal "POST", @response.headers["X-Method"]
+ assert_equal "/complete", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ assert_match(/remember_me/, @response.headers["X-Query-String"])
+ end
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/complete", @response.headers["X-Path"]
- assert_equal "success", @response.body
- assert_match(/remember_me/, @response.headers["X-Query-String"])
- end
+ it "with_post_method_custom_return_to" do
+ app = app(return_to: "http://example.org/complete")
+ mock_openid_request(app, "/", method: "POST")
+ follow_openid_redirect!(app)
- it "with_post_nested_params_custom_return_to" do
- url = "http://example.org/complete?user[remember_me]=true"
- @app = app(return_to: url)
- process("/", method: "POST")
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/complete", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ end
- assert_equal 303, @response.status
- env = Rack::MockRequest.env_for(@response.headers["Location"])
- _status, headers, _body = RotsApp.call(env)
+ it "with_custom_return_method" do
+ app = app(method: "put")
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
- _uri, input = headers["Location"].split("?", 2)
- process("http://example.org/complete?user[remember_me]=true", method: "POST", input: input)
+ assert_equal 200, @response.status
+ assert_equal "PUT", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ end
- assert_equal 200, @response.status
- assert_equal "POST", @response.headers["X-Method"]
- assert_equal "/complete", @response.headers["X-Path"]
- assert_equal "success", @response.body
- assert_match(/remember_me/, @response.headers["X-Query-String"])
- end
+ it "with_simple_registration_fields" do
+ app = app(required: ["nickname", "email"], optional: "fullname")
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
- it "with_post_method_custom_return_to" do
- @app = app(return_to: "http://example.org/complete")
- process("/", method: "POST")
- follow_redirect!
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ end
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/complete", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ it "with_attribute_exchange" do
+ app = app(
+ required: ["http://axschema.org/namePerson/friendly", "http://axschema.org/contact/email"],
+ optional: "http://axschema.org/namePerson",
+ )
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
+
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ end
- it "with_custom_return_method" do
- @app = app(method: "put")
- process("/", method: "GET")
- follow_redirect!
+ it "with_oauth" do
+ app = app(
+ "oauth[consumer]": "www.example.com",
+ "oauth[scope]": ["http://docs.google.com/feeds/", "http://spreadsheets.google.com/feeds/"],
+ )
+ mock_openid_request(app, "/", method: "GET")
- assert_equal 200, @response.status
- assert_equal "PUT", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ location = @response.headers["Location"]
- it "with_simple_registration_fields" do
- @app = app(required: ["nickname", "email"], optional: "fullname")
- process("/", method: "GET")
- follow_redirect!
+ assert_match(/openid.oauth.consumer/, location)
+ assert_match(/openid.oauth.scope/, location)
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ follow_openid_redirect!(app)
- it "with_attribute_exchange" do
- @app = app(
- required: ["http://axschema.org/namePerson/friendly", "http://axschema.org/contact/email"],
- optional: "http://axschema.org/namePerson",
- )
- process("/", method: "GET")
- follow_redirect!
-
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ end
- it "with_oauth" do
- @app = app(
- "oauth[consumer]": "www.example.com",
- "oauth[scope]": ["http://docs.google.com/feeds/", "http://spreadsheets.google.com/feeds/"],
- )
- process("/", method: "GET")
+ it "with_pape" do
+ app = app(
+ "pape[preferred_auth_policies]": ["test_policy1", "test_policy2"],
+ "pape[max_auth_age]": 600,
+ )
+ mock_openid_request(app, "/", method: "GET")
- location = @response.headers["Location"]
+ location = @response.headers["Location"]
- assert_match(/openid.oauth.consumer/, location)
- assert_match(/openid.oauth.scope/, location)
+ assert_match(/pape\.preferred_auth_policies=test_policy1\+test_policy2/, location)
+ assert_match(/pape\.max_auth_age=600/, location)
- follow_redirect!
+ follow_openid_redirect!(app)
- assert_equal 200, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
- end
+ assert_equal 200, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "success", @response.body
+ end
- it "with_pape" do
- @app = app(
- "pape[preferred_auth_policies]": ["test_policy1", "test_policy2"],
- "pape[max_auth_age]": 600,
- )
- process("/", method: "GET")
+ it "with_immediate_mode_setup_needed" do
+ skip("because failing, and not enough time to fix all the things") do
+ app = app(identifier: "#{Rots::Mocks::RotsServer::SERVER_URL}/john.doe?openid.success=false", immediate: true)
+ mock_openid_request(app, "/", method: "GET")
location = @response.headers["Location"]
- assert_match(/pape\.preferred_auth_policies=test_policy1\+test_policy2/, location)
- assert_match(/pape\.max_auth_age=600/, location)
+ assert_match(/openid.mode=checkid_immediate/, location)
- follow_redirect!
+ follow_openid_redirect!(app)
- assert_equal 200, @response.status
+ assert_equal 307, @response.status
assert_equal "GET", @response.headers["X-Method"]
assert_equal "/", @response.headers["X-Path"]
- assert_equal "success", @response.body
+ assert_equal Rots::Mocks::RotsServer::SERVER_URL, @response.headers["Location"]
+ assert_equal "setup_needed", @response.body
end
+ end
- it "with_immediate_mode_setup_needed" do
- skip("because failing, and not enough time to fix all the things") do
- @app = app(identifier: "#{ROTS_SERVER_URL}/john.doe?openid.success=false", immediate: true)
- process("/", method: "GET")
-
- location = @response.headers["Location"]
-
- assert_match(/openid.mode=checkid_immediate/, location)
-
- follow_redirect!
-
- assert_equal 307, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal ROTS_SERVER_URL, @response.headers["Location"]
- assert_equal "setup_needed", @response.body
- end
- end
-
- it "with_realm_wildcard" do
- @app = app(
- realm_domain: "*.example.org",
- )
- process("/", method: "GET")
-
- location = @response.headers["Location"]
-
- assert_match(/openid.realm=http%3A%2F%2F%2A.example.org/, location)
-
- follow_redirect!
-
- assert_equal 200, @response.status
- end
-
- it "with_inferred_realm" do
- @app = app({})
- process("/", method: "GET")
-
- location = @response.headers["Location"]
-
- assert_match(/openid.realm=http%3A%2F%2Fexample.org/, location)
-
- follow_redirect!
-
- assert_equal 200, @response.status
- end
+ it "with_realm_wildcard" do
+ app = app(
+ realm_domain: "*.example.org",
+ )
+ mock_openid_request(app, "/", method: "GET")
- it "with_missing_id" do
- @app = app(identifier: "#{ROTS_SERVER_URL}/john.doe")
- process("/", method: "GET")
- follow_redirect!
+ location = @response.headers["Location"]
- assert_equal 400, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "cancel", @response.body
- end
+ assert_match(/openid.realm=http%3A%2F%2F%2A.example.org/, location)
- it "with_timeout" do
- @app = app(identifier: ROTS_SERVER_URL)
- process("/", method: "GET")
+ follow_openid_redirect!(app)
- assert_equal 400, @response.status
- assert_equal "GET", @response.headers["X-Method"]
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "missing", @response.body
- end
+ assert_equal 200, @response.status
+ end
- it "sanitize_query_string" do
- @app = app({})
- process("/", method: "GET")
- follow_redirect!
+ it "with_inferred_realm" do
+ app = app({})
+ mock_openid_request(app, "/", method: "GET")
- assert_equal 200, @response.status
- assert_equal "/", @response.headers["X-Path"]
- assert_equal "", @response.headers["X-Query-String"]
- end
+ location = @response.headers["Location"]
- it "passthrough_standard_http_basic_auth" do
- @app = app({})
- process("/", :method => "GET", "MOCK_HTTP_BASIC_AUTH" => "1")
+ assert_match(/openid.realm=http%3A%2F%2Fexample.org/, location)
- assert_equal 401, @response.status
- end
+ follow_openid_redirect!(app)
- private
-
- def app(options = {})
- options[:identifier] ||= "#{ROTS_SERVER_URL}/john.doe?openid.success=true"
-
- rack_app = lambda { |env|
- if (resp = env[Rack::OpenID::RESPONSE])
- headers = {
- "X-Path" => env["PATH_INFO"],
- "X-Method" => env["REQUEST_METHOD"],
- "X-Query-String" => env["QUERY_STRING"],
- }
- if resp.status == :success
- [200, headers, [resp.status.to_s]]
- elsif resp.status == :setup_needed
- headers["Location"] = ROTS_SERVER_URL # TODO update Rots to properly send user_setup_url. This should come from resp.
- [307, headers, [resp.status.to_s]]
- else
- [400, headers, [resp.status.to_s]]
- end
- elsif env["MOCK_HTTP_BASIC_AUTH"]
- [401, {Rack::OpenID::AUTHENTICATE_HEADER => 'Realm="Example"'}, []]
- else
- [401, {Rack::OpenID::AUTHENTICATE_HEADER => Rack::OpenID.build_header(options)}, []]
- end
- }
- Rack::Session::Pool.new(Rack::OpenID.new(rack_app))
- end
+ assert_equal 200, @response.status
end
- describe "simple auth" do
- include RackTestHelpers
-
- it "can login" do
- @app = simple_app("#{ROTS_SERVER_URL}/john.doe?openid.success=true")
+ it "with_missing_id" do
+ app = app(identifier: "#{Rots::Mocks::RotsServer::SERVER_URL}/john.doe")
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
- process "/dashboard"
- follow_redirect!
+ assert_equal 400, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "cancel", @response.body
+ end
- assert_equal 303, @response.status
- assert_equal "http://example.org/dashboard", @response.headers["Location"]
+ it "with_timeout" do
+ app = app(identifier: Rots::Mocks::RotsServer::SERVER_URL)
+ mock_openid_request(app, "/", method: "GET")
- cookie = @response.headers["Set-Cookie"].split(";").first
- process "/dashboard", "HTTP_COOKIE" => cookie
+ assert_equal 400, @response.status
+ assert_equal "GET", @response.headers["X-Method"]
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "missing", @response.body
+ end
- assert_equal 200, @response.status
- assert_equal "Hello", @response.body
- end
+ it "sanitize_query_string" do
+ app = app({})
+ mock_openid_request(app, "/", method: "GET")
+ follow_openid_redirect!(app)
- it "fails login" do
- @app = simple_app("#{ROTS_SERVER_URL}/john.doe")
- @app = simple_app("#{ROTS_SERVER_URL}/john.doe")
+ assert_equal 200, @response.status
+ assert_equal "/", @response.headers["X-Path"]
+ assert_equal "", @response.headers["X-Query-String"]
+ end
- process "/dashboard"
- follow_redirect!
+ it "passthrough_standard_http_basic_auth" do
+ app = app({})
+ mock_openid_request(app, "/", :method => "GET", "MOCK_HTTP_BASIC_AUTH" => "1")
- assert_match ROTS_SERVER_URL, @response.headers["Location"]
- end
+ assert_equal 401, @response.status
+ end
- private
+ private
- def simple_app(identifier)
- rack_app = lambda { |env| [200, {"Content-Type" => "text/html"}, ["Hello"]] }
- rack_app = Rack::OpenID::SimpleAuth.new(rack_app, identifier)
- Rack::Session::Pool.new(rack_app)
- end
+ def app(options = {})
+ Rots::Mocks::ClientApp.new(**options)
end
end
diff --git a/test/test_openid_headers.rb b/test/test_openid_headers.rb
new file mode 100644
index 0000000..47d0e66
--- /dev/null
+++ b/test/test_openid_headers.rb
@@ -0,0 +1,46 @@
+require_relative "support/config"
+
+describe "openid headers" do
+ it "builds header" do
+ assert_equal 'OpenID identity="http://example.com/"',
+ Rack::OpenID.build_header(identity: "http://example.com/")
+ assert_equal 'OpenID identity="http://example.com/?foo=bar"',
+ Rack::OpenID.build_header(identity: "http://example.com/?foo=bar")
+
+ header = Rack::OpenID.build_header(identity: "http://example.com/", return_to: "http://example.org/")
+
+ assert_match(/OpenID /, header)
+ assert_match(/identity="http:\/\/example\.com\/"/, header)
+ assert_match(/return_to="http:\/\/example\.org\/"/, header)
+
+ header = Rack::OpenID.build_header(identity: "http://example.com/", required: ["nickname", "email"])
+
+ assert_match(/OpenID /, header)
+ assert_match(/identity="http:\/\/example\.com\/"/, header)
+ assert_match(/required="nickname,email"/, header)
+ end
+
+ it "parses header" do
+ assert_equal(
+ {"identity" => "http://example.com/"},
+ Rack::OpenID.parse_header('OpenID identity="http://example.com/"'),
+ )
+ assert_equal(
+ {"identity" => "http://example.com/?foo=bar"},
+ Rack::OpenID.parse_header('OpenID identity="http://example.com/?foo=bar"'),
+ )
+ assert_equal(
+ {"identity" => "http://example.com/", "return_to" => "http://example.org/"},
+ Rack::OpenID.parse_header('OpenID identity="http://example.com/", return_to="http://example.org/"'),
+ )
+ assert_equal(
+ {"identity" => "http://example.com/", "required" => ["nickname", "email"]},
+ Rack::OpenID.parse_header('OpenID identity="http://example.com/", required="nickname,email"'),
+ )
+
+ # ensure we don't break standard HTTP basic auth
+ assert_empty(
+ Rack::OpenID.parse_header('Realm="Example"'),
+ )
+ end
+end
diff --git a/test/test_rack_openid.rb b/test/test_rack_openid.rb
index 80d5679..4c87d28 100644
--- a/test/test_rack_openid.rb
+++ b/test/test_rack_openid.rb
@@ -1,4 +1,4 @@
-require_relative "test_helper"
+require_relative "support/config"
describe Rack::OpenID do
describe ".sanitize_params!" do
diff --git a/test/test_simple_auth.rb b/test/test_simple_auth.rb
new file mode 100644
index 0000000..cd316c2
--- /dev/null
+++ b/test/test_simple_auth.rb
@@ -0,0 +1,37 @@
+require_relative "support/config"
+
+describe "simple auth" do
+ include Rots::Test::RackTestHelpers
+
+ it "can login" do
+ app = simple_app("#{Rots::Mocks::RotsServer::SERVER_URL}/john.doe?openid.success=true")
+ mock_openid_request app, "/dashboard"
+ follow_openid_redirect!(app)
+
+ assert_equal 303, @response.status
+ assert_equal "http://example.org/dashboard", @response.headers["Location"]
+
+ cookie = @response.headers["Set-Cookie"].split(";").first
+ mock_openid_request app, "/dashboard", "HTTP_COOKIE" => cookie
+
+ assert_equal 200, @response.status
+ assert_equal "Hello", @response.body
+ end
+
+ it "fails login" do
+ app = simple_app("#{Rots::Mocks::RotsServer::SERVER_URL}/john.doe")
+
+ mock_openid_request app, "/dashboard"
+ follow_openid_redirect!(app)
+
+ assert_match Rots::Mocks::RotsServer::SERVER_URL, @response.headers["Location"]
+ end
+
+ private
+
+ def simple_app(identifier)
+ rack_app = lambda { |env| [200, {"Content-Type" => "text/html"}, ["Hello"]] }
+ rack_app = Rack::OpenID::SimpleAuth.new(rack_app, identifier)
+ Rack::Session::Pool.new(rack_app)
+ end
+end