pax_global_header00006660000000000000000000000064145427540230014520gustar00rootroot0000000000000052 comment=80cb364d6c710477bc6e08d09da616532a741495 SWD-2.0.3/000077500000000000000000000000001454275402300121575ustar00rootroot00000000000000SWD-2.0.3/.github/000077500000000000000000000000001454275402300135175ustar00rootroot00000000000000SWD-2.0.3/.github/FUNDING.yml000066400000000000000000000000731454275402300153340ustar00rootroot00000000000000# These are supported funding model platforms github: nov SWD-2.0.3/.github/workflows/000077500000000000000000000000001454275402300155545ustar00rootroot00000000000000SWD-2.0.3/.github/workflows/spec.yml000066400000000000000000000011031454275402300172240ustar00rootroot00000000000000name: Spec on: push: branches: - main pull_request: permissions: contents: read jobs: spec: strategy: matrix: os: ['ubuntu-20.04', 'ubuntu-22.04'] ruby-version: ['3.1', '3.2', '3.3'] include: - os: 'ubuntu-20.04' ruby-version: '3.0' runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run Specs run: bundle exec rake spec SWD-2.0.3/.gitignore000066400000000000000000000002231454275402300141440ustar00rootroot00000000000000## MAC OS .DS_Store ## TEXTMATE *.tmproj tmtags ## EMACS *~ \#* .\#* ## VIM *.swp ## PROJECT::GENERAL coverage* rdoc pkg ## PROJECT::SPECIFIC SWD-2.0.3/.rspec000066400000000000000000000000371454275402300132740ustar00rootroot00000000000000--color --format=documentation SWD-2.0.3/.travis.yml000066400000000000000000000001261454275402300142670ustar00rootroot00000000000000before_install: - gem install bundler rvm: - 2.6.10 - 2.7.6 - 3.0.4 - 3.1.2SWD-2.0.3/CHANGELOG.md000066400000000000000000000011041454275402300137640ustar00rootroot00000000000000## [2.0.2] - 2022-10-09 ### Changed * convert Faraday::Error to SWD::Exception https://github.com/nov/SWD/commit/a5bacba3c0fe740b1e36be220980b29dc50de095 ### Fixed * SWD::Resource#to_response_object was needed for openid_connect gem https://github.com/nov/SWD/commit/c4d916d3cd8d4fe076bd27a64403d98dea9b00ef ## [2.0.1] - 2022-10-08 ### Fixed * typo https://github.com/nov/SWD/commit/3fd7a4fbe8fdf586824edc5d2b4222c43e7a9655 ## [2.0.0] - 2022-10-08 ### Added - start recording CHANGELOG ### Changed - Switch from httpclient to faraday https://github.com/nov/SWD/pull/4SWD-2.0.3/Gemfile000066400000000000000000000001361454275402300134520ustar00rootroot00000000000000source "http://rubygems.org" platform :jruby do gem 'jruby-openssl', '>= 0.7' end gemspec SWD-2.0.3/LICENSE000066400000000000000000000020361454275402300131650ustar00rootroot00000000000000Copyright (c) 2011 nov matake Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SWD-2.0.3/README.rdoc000066400000000000000000000023271454275402300137710ustar00rootroot00000000000000= SWD SWD (Simple Web Discovery) Client Library NOTE: SWD was designed to be the core of OpenID Connect Discovery, but it's replaced with WebFinger. So I'm no longer supporting this gem. == Installation gem install swd == Resources * View Source on GitHub (https://github.com/nov/swd) * Report Issues on GitHub (https://github.com/nov/swd/issues) == Examples begin res = SWD.discover!( :principal => 'nov@matake.jp', :service => 'http://openid.net/specs/connect/1.0/issuer', :host => 'openid-connect.herokuapp.com' ) # => SWD::Response puts res.location, res.locations, res.raw rescue SWD::HttpError => e puts "#{e.status} #{e.message}" rescue SWD::Exception => e puts e.message end == Note on Patches/Pull Requests * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. == Copyright Copyright (c) 2011 nov matake. See LICENSE for details. SWD-2.0.3/Rakefile000066400000000000000000000005701454275402300136260ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) namespace :coverage do desc 'Open coverage report' task :report do require 'simplecov' `open '#{File.join SimpleCov.coverage_path, 'index.html'}'` end end task :spec do Rake::Task[:'coverage:report'].invoke unless ENV['TRAVIS_RUBY_VERSION'] end task default: :specSWD-2.0.3/VERSION000066400000000000000000000000051454275402300132220ustar00rootroot000000000000002.0.3SWD-2.0.3/lib/000077500000000000000000000000001454275402300127255ustar00rootroot00000000000000SWD-2.0.3/lib/swd.rb000066400000000000000000000031721454275402300140520ustar00rootroot00000000000000require 'json' require 'logger' require 'openssl' require 'active_support' require 'active_support/core_ext' require 'faraday' require 'faraday/follow_redirects' require 'attr_required' require 'attr_optional' module SWD VERSION = ::File.read( ::File.join(::File.dirname(__FILE__), '../VERSION') ).strip def self.cache=(cache) @@cache = cache end def self.cache @@cache end def self.discover!(attributes = {}) Resource.new(attributes).discover!(attributes[:cache]) end def self.logger @@logger end def self.logger=(logger) @@logger = logger end self.logger = ::Logger.new(STDOUT) self.logger.progname = 'SWD' def self.debugging? @@debugging end def self.debugging=(boolean) @@debugging = boolean end def self.debug! self.debugging = true end def self.debug(&block) original = self.debugging? self.debugging = true yield ensure self.debugging = original end self.debugging = false def self.http_client Faraday.new(headers: {user_agent: "SWD #{VERSION}"}) do |faraday| faraday.response :raise_error faraday.response :json faraday.response :follow_redirects faraday.response :logger, SWD.logger if debugging? faraday.adapter Faraday.default_adapter http_config.try(:call, faraday) end end def self.http_config(&block) @@http_config ||= block end def self.url_builder @@url_builder ||= URI::HTTPS end def self.url_builder=(builder) @@url_builder = builder end end require 'swd/cache' require 'swd/exception' require 'swd/resource' require 'swd/response' SWD.cache = SWD::Cache.new SWD-2.0.3/lib/swd/000077500000000000000000000000001454275402300135225ustar00rootroot00000000000000SWD-2.0.3/lib/swd/cache.rb000066400000000000000000000001351454275402300151110ustar00rootroot00000000000000module SWD class Cache def fetch(cache_key, options = {}) yield end end endSWD-2.0.3/lib/swd/exception.rb000066400000000000000000000014221454275402300160440ustar00rootroot00000000000000module SWD class Exception < StandardError; end class HttpError < Exception attr_accessor :status, :response def initialize(status, message = nil, response = nil) super message @status = status @response = response end end class BadRequest < HttpError def initialize(message = nil, response = nil) super 400, message, response end end class Unauthorized < HttpError def initialize(message = nil, response = nil) super 401, message, response end end class Forbidden < HttpError def initialize(message = nil, response = nil) super 403, message, response end end class NotFound < HttpError def initialize(message = nil, response = nil) super 404, message, response end end endSWD-2.0.3/lib/swd/resource.rb000066400000000000000000000041041454275402300156750ustar00rootroot00000000000000module SWD class Resource include AttrRequired, AttrOptional attr_required :principal, :service, :host, :path attr_optional :port class Expired < Exception; end def initialize(attributes = {}) (optional_attributes + required_attributes).each do |key| self.send "#{key}=", attributes[key] end @path ||= '/.well-known/simple-web-discovery' attr_missing! end def discover!(cache_options = {}) SWD.cache.fetch(cache_key, cache_options) do handle_response do SWD.http_client.get endpoint.to_s end end end def endpoint SWD.url_builder.build [nil, host, port, path, { :principal => principal, :service => service }.to_query, nil] rescue URI::Error => e raise Exception.new(e.message) end private def handle_response json = yield.body.with_indifferent_access if redirect = json[:SWD_service_redirect] redirect_to redirect[:location], redirect[:expires] else to_response_object json end rescue Faraday::Error => e case e.response_status when nil raise Exception.new e when 400 raise BadRequest.new('Bad Request', e.response_body) when 401 raise Unauthorized.new('Unauthorized', e.response_body) when 403 raise Forbidden.new('Forbidden', e.response_body) when 404 raise NotFound.new('Not Found', e.response_body) else raise HttpError.new(e.response_status, e.response_body, e.response_body) end end # NOTE: overwritten in openid_connect gem. def to_response_object(json) Response.new json end def redirect_to(location, expires) uri = URI.parse(location) @host, @path, @port = uri.host, uri.path, uri.port raise Expired if expires && expires.to_i < Time.now.utc.to_i discover! end def cache_key sha256 = OpenSSL::Digest::SHA256.hexdigest [ principal, service, host ].join(' ') "swd:resource:#{sha256}" end end end SWD-2.0.3/lib/swd/response.rb000066400000000000000000000003161454275402300157050ustar00rootroot00000000000000module SWD class Response attr_accessor :locations, :location, :raw def initialize(hash) @locations = hash[:locations] @location = @locations.first @raw = hash end end endSWD-2.0.3/spec/000077500000000000000000000000001454275402300131115ustar00rootroot00000000000000SWD-2.0.3/spec/helpers/000077500000000000000000000000001454275402300145535ustar00rootroot00000000000000SWD-2.0.3/spec/helpers/webmock_helper.rb000066400000000000000000000015701454275402300200710ustar00rootroot00000000000000require 'webmock/rspec' module WebMockHelper def mock_json(endpoint, response_file, options = {}) endpoint = endpoint.to_s stub_request(:get, endpoint).with( request_for(options) ).to_return( response_for(response_file, options) ) yield a_request(:get, endpoint).with( request_for(options) ).should have_been_made.once end private def request_for(options = {}) request = {} request[:query] = options[:query] request end def response_for(response_file, options = {}) response = {} response[:headers] = { 'Content-Type': 'application/json' } response[:body] = File.new(File.join(File.dirname(__FILE__), '../mock_json', "#{response_file}.json")) if options[:status] response[:status] = options[:status] end response end end include WebMockHelper WebMock.disable_net_connect!SWD-2.0.3/spec/mock_json/000077500000000000000000000000001454275402300150735ustar00rootroot00000000000000SWD-2.0.3/spec/mock_json/blank.json000066400000000000000000000000001454275402300170430ustar00rootroot00000000000000SWD-2.0.3/spec/mock_json/redirect.json000066400000000000000000000001321454275402300175630ustar00rootroot00000000000000{ "SWD_service_redirect": { "location": "https://swd.proseware.com/swd_server" } }SWD-2.0.3/spec/mock_json/redirect_expired.json000066400000000000000000000001651454275402300213110ustar00rootroot00000000000000{ "SWD_service_redirect": { "location": "https://swd.proseware.com/swd_server", "expires": 1300752001 } }SWD-2.0.3/spec/mock_json/redirect_with_port.json000066400000000000000000000001371454275402300216670ustar00rootroot00000000000000{ "SWD_service_redirect": { "location": "https://swd.proseware.com:8080/swd_server" } }SWD-2.0.3/spec/mock_json/success.json000066400000000000000000000001061454275402300174330ustar00rootroot00000000000000{ "locations": ["http://calendars.proseware.com/calendars/joseph"] }SWD-2.0.3/spec/spec_helper.rb000066400000000000000000000004001454275402300157210ustar00rootroot00000000000000require 'simplecov' SimpleCov.start do add_filter 'spec' end require 'rspec' require 'rspec/its' require 'swd' RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = [:should, :expect] end end require 'helpers/webmock_helper'SWD-2.0.3/spec/swd/000077500000000000000000000000001454275402300137065ustar00rootroot00000000000000SWD-2.0.3/spec/swd/exception_spec.rb000066400000000000000000000011221454275402300172370ustar00rootroot00000000000000require 'spec_helper' describe SWD::HttpError do subject do SWD::HttpError.new 400, 'Bad Request', 'Message' end its(:status) { should == 400 } its(:message) { should == 'Bad Request' } its(:response) { should == 'Message' } end describe SWD::BadRequest do its(:status) { should == 400 } its(:message) { should == 'SWD::BadRequest' } end describe SWD::Unauthorized do its(:status) { should == 401 } its(:message) { should == 'SWD::Unauthorized' } end describe SWD::Forbidden do its(:status) { should == 403 } its(:message) { should == 'SWD::Forbidden' } endSWD-2.0.3/spec/swd/resource_spec.rb000066400000000000000000000111471454275402300171000ustar00rootroot00000000000000require 'spec_helper' describe SWD::Resource do subject { resource } let(:resource) { SWD::Resource.new attributes } let(:attributes) do { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar', :host => 'example.com' } end its(:path) { should == '/.well-known/simple-web-discovery' } [:principal, :service, :host].each do |key| it "should require #{key}" do expect do SWD::Resource.new attributes.merge(key => nil) end.to raise_error AttrRequired::AttrMissing end end describe '#discover!' do context 'when succeeded' do it 'should return SWD::Response' do mock_json resource.endpoint, 'success' do res = resource.discover! res.should be_a SWD::Response res.locations == ['http://calendars.proseware.com/calendars/joseph'] res.location == 'http://calendars.proseware.com/calendars/joseph' res.raw == { 'locations' => ['http://calendars.proseware.com/calendars/joseph'] } end end end context 'when redirected' do it 'should follow redirect' do expect(resource).to receive(:redirect_to).with( 'https://swd.proseware.com/swd_server', nil ) mock_json resource.endpoint, 'redirect' do resource.discover! end end context 'when expired' do it 'should return SWD::Response' do mock_json resource.endpoint, 'redirect_expired' do expect { res = resource.discover! }.to raise_error SWD::Resource::Expired end end end context 'otherwise' do it 'should return SWD::Response' do mock_json resource.endpoint, 'redirect' do mock_json 'https://swd.proseware.com/swd_server', 'success', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do res = resource.discover! res.should be_a SWD::Response end end end end end describe 'error handling' do before(:all) do module SWD class << self module HttpClientWithCached def http_client @http_client ||= super end end prepend HttpClientWithCached end end end after(:all) do module SWD class << self module HttpClientWithCacheCleared def http_client @http_client = nil super end end prepend HttpClientWithCacheCleared end end end context 'when Faraday::Error without response' do it do expect(SWD.http_client).to receive(:get).and_raise(Faraday::Error) expect { res = resource.discover! }.to raise_error SWD::Exception end end context 'when bad request' do it 'should raise SWD::BadRequest' do mock_json resource.endpoint, 'blank', :status => 400 do expect { res = resource.discover! }.to raise_error SWD::BadRequest end end end context 'when unauthorized' do it 'should raise SWD::Unauthorized' do mock_json resource.endpoint, 'blank', :status => 401 do expect { res = resource.discover! }.to raise_error SWD::Unauthorized end end end context 'when forbidden' do it 'should raise SWD::Forbidden' do mock_json resource.endpoint, 'blank', :status => 403 do expect { res = resource.discover! }.to raise_error SWD::Forbidden end end end context 'when not found' do it 'should raise SWD::NotFound' do mock_json resource.endpoint, 'blank', :status => 404 do expect { res = resource.discover! }.to raise_error SWD::NotFound end end end context 'when other error happened' do it 'should raise SWD::HttpError' do mock_json resource.endpoint, 'blank', :status => 500 do expect { res = resource.discover! }.to raise_error SWD::HttpError end end end end end describe '#endpoint' do its(:endpoint) { should be_instance_of URI::HTTPS } context 'with URI::HTTP builder' do before do @original_url_builder = SWD.url_builder SWD.url_builder = URI::HTTP end after do SWD.url_builder = @original_url_builder end its(:endpoint) { should be_instance_of URI::HTTP } end end endSWD-2.0.3/spec/swd_spec.rb000066400000000000000000000072201454275402300152460ustar00rootroot00000000000000require 'spec_helper' describe SWD do after { SWD.debugging = false } its(:logger) { should be_a Logger } its(:debugging?) { should == false } its(:cache) { should be_a SWD::Cache } describe '#discover!' do it 'should return SWD::Response' do mock_json "https://example.com/.well-known/simple-web-discovery", 'success', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do SWD.discover!( :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar', :host => 'example.com' ).should be_a SWD::Response end end context 'when port specified' do it 'should use it' do mock_json "https://example.com:8080/.well-known/simple-web-discovery", 'success', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do SWD.discover!( :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar', :host => 'example.com', :port => 8080 ).should be_a SWD::Response end end context 'when redirected to different host' do context 'with port' do it 'should success' do mock_json "https://example.com:8080/.well-known/simple-web-discovery", 'redirect_with_port', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do mock_json "https://swd.proseware.com:8080/swd_server", 'success', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do SWD.discover!( :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar', :host => 'example.com', :port => 8080 ).should be_a SWD::Response end end end end context 'without port' do it 'should success' do mock_json "https://example.com:8080/.well-known/simple-web-discovery", 'redirect', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do mock_json "https://swd.proseware.com/swd_server", 'success', :query => { :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar' } do SWD.discover!( :principal => 'mailto:joe@example.com', :service => 'urn:adatum.com:calendar', :host => 'example.com', :port => 8080 ).should be_a SWD::Response end end end end end end end describe '.debug!' do before { SWD.debug! } its(:debugging?) { should == true } end describe '.debug' do it 'should enable debugging within given block' do SWD.debug do SWD.debugging?.should == true end SWD.debugging?.should == false end it 'should not force disable debugging' do SWD.debug! SWD.debug do SWD.debugging?.should == true end SWD.debugging?.should == true end end describe '.http_client' do context 'with http_config' do before do SWD.http_config do |config| config.ssl.verify = false end end it 'should configure http_client' do SWD.http_client.ssl.verify.should be_falsy end end end endSWD-2.0.3/swd.gemspec000066400000000000000000000017301454275402300143220ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = 'swd' s.version = File.read('VERSION') s.authors = ['nov matake'] s.email = ['nov@matake.jp'] s.homepage = 'https://github.com/nov/swd' s.summary = %q{SWD (Simple Web Discovery) Client Library} s.description = %q{SWD (Simple Web Discovery) Client Library} s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ['lib'] s.add_runtime_dependency 'faraday', '~> 2.0' s.add_runtime_dependency 'faraday-follow_redirects' s.add_runtime_dependency 'activesupport', '>= 3' s.add_runtime_dependency 'attr_required', '>= 0.0.5' s.add_development_dependency 'rake' s.add_development_dependency 'rspec' s.add_development_dependency 'rspec-its' s.add_development_dependency 'webmock' s.add_development_dependency 'simplecov' end