gds-sso 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gds-sso.gemspec
4
+ gemspec
@@ -0,0 +1,35 @@
1
+ ## Introduction
2
+
3
+ GDS-SSO provides everything needed to integrate an application with the sign-on-o-tron single-sign-on
4
+ (https://github.com/alphagov/sign-on-o-tron) as used by the Government Digital Service, though it
5
+ will probably also work with a range of other oauth2 providers.
6
+
7
+ It is a wrapper around omniauth that adds a 'strategy' for oAuth2 integration against sign-on-o-tron,
8
+ and the necessary controller to support that request flow.
9
+
10
+ For more details on OmniAuth and oAuth2 integration see https://github.com/intridea/omniauth
11
+
12
+
13
+ ## Integration with a Rails 3+ app
14
+
15
+ To use gds-sso tou will need an oauth client ID and secret for sign-on-o-tron or a compatible system.
16
+ These can be provided by one of the team with admin access to sign-on-o-tron.
17
+
18
+ Then include the gem in your Gemfile:
19
+
20
+ gem 'gds-sso', :git => 'https://github.com/alphagov/gds-sso.git'
21
+
22
+ Create a `config/initializers/gds-sso.rb` that looks like:
23
+
24
+ GDS::SSO.config do |config|
25
+ config.user_model = 'User'
26
+ # set up ID and Secret in a way which doesn't require it to be checked in to source control...
27
+ config.oauth_id = ENV['OAUTH_ID']
28
+ config.oauth_secret = ENV['OAUTH_SECRET']
29
+ # optional config for location of sign-on-o-tron
30
+ config.oauth_root_url = "http://localhost:3001"
31
+ end
32
+
33
+ The user model needs to respond to klass.find_by_uid(uid), and must include the GDS::SSO::User module.
34
+
35
+ You also need to include `GDS::SSO::ControllerMethods` in your ApplicationController
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/test*.rb']
9
+ t.verbose = true
10
+ end
@@ -0,0 +1,41 @@
1
+ require 'rails'
2
+
3
+ require 'gds-sso/config'
4
+ require 'gds-sso/omniauth_strategy'
5
+ require 'gds-sso/warden_config'
6
+ require 'gds-sso/routes'
7
+
8
+ module GDS
9
+ module SSO
10
+ autoload :FailureApp, 'gds-sso/failure_app'
11
+ autoload :ControllerMethods, 'gds-sso/controller_methods'
12
+ autoload :User, 'gds-sso/user'
13
+
14
+ def self.config
15
+ yield GDS::SSO::Config
16
+ end
17
+
18
+ def self.default_strategy
19
+ if ['development', 'test'].include?(Rails.env) && ENV['GDS_SSO_STRATEGY'] != 'real'
20
+ :mock_gds_sso
21
+ else
22
+ :gds_sso
23
+ end
24
+ end
25
+
26
+ class Engine < ::Rails::Engine
27
+ # Force routes to be loaded if we are doing any eager load.
28
+ # TODO - check this one - Stolen from Devise because it looked sensible...
29
+ config.before_eager_load { |app| app.reload_routes! }
30
+
31
+ config.app_middleware.use ::OmniAuth::Builder do
32
+ provider :gds, GDS::SSO::Config.oauth_id, GDS::SSO::Config.oauth_secret
33
+ end
34
+
35
+ config.app_middleware.use Warden::Manager do |manager|
36
+ manager.default_strategies GDS::SSO.default_strategy
37
+ manager.failure_app = GDS::SSO::FailureApp
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ module GDS
2
+ module SSO
3
+ module Config
4
+ # Name of the User class
5
+ mattr_accessor :user_model
6
+ @@user_model = "User"
7
+
8
+ # OAuth ID
9
+ mattr_accessor :oauth_id
10
+
11
+ # OAuth Secret
12
+ mattr_accessor :oauth_secret
13
+
14
+ # Location of the OAuth server
15
+ mattr_accessor :oauth_root_url
16
+ @@oauth_root_url = "http://localhost:3001"
17
+
18
+ def self.user_klass
19
+ user_model.to_s.constantize
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module GDS
2
+ module SSO
3
+ module ControllerMethods
4
+ def authenticate_user!
5
+ warden.authenticate!
6
+ end
7
+
8
+ def user_signed_in?
9
+ warden.authenticated?
10
+ end
11
+
12
+ def current_user
13
+ warden.user if user_signed_in?
14
+ end
15
+
16
+ def log_out
17
+ warden.log_out
18
+ end
19
+
20
+ def warden
21
+ request.env['warden']
22
+ end
23
+
24
+ def self.included(base)
25
+ base.helper_method :user_signed_in?
26
+ base.helper_method :current_user
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ require "action_controller/metal"
2
+ require 'rails'
3
+
4
+ # Failure application that will be called every time :warden is thrown from
5
+ # any strategy or hook.
6
+ module GDS
7
+ module SSO
8
+ class FailureApp < ActionController::Metal
9
+ include ActionController::RackDelegation
10
+ include ActionController::UrlFor
11
+ include ActionController::Redirecting
12
+ include Rails.application.routes.url_helpers
13
+
14
+ def self.call(env)
15
+ action(:respond).call(env)
16
+ end
17
+
18
+ def respond
19
+ redirect
20
+ end
21
+
22
+ def redirect
23
+ store_location!
24
+ redirect_to '/auth/gds'
25
+ end
26
+
27
+ # Stores requested uri to redirect the user after signing in. We cannot use
28
+ # scoped session provided by warden here, since the user is not authenticated
29
+ # yet, but we still need to store the uri based on scope, so different scopes
30
+ # would never use the same uri to redirect.
31
+
32
+ # TOTALLY NOT DOING THE SCOPE THING. PROBABLY SHOULD.
33
+ def store_location!
34
+ session["return_to"] = env['warden.options'][:attempted_path] if request.get?
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ require 'omniauth/oauth'
2
+ require 'multi_json'
3
+
4
+ # Authenticate to GDS with OAuth 2.0 and retrieve
5
+ # basic user information.
6
+ #
7
+ # @example Basic Usage
8
+ # use OmniAuth::Builder :gds, 'API Key', 'Secret Key'
9
+
10
+ class OmniAuth::Strategies::Gds < OmniAuth::Strategies::OAuth2
11
+ # @param [Rack Application] app standard middleware application parameter
12
+ # @param [String] api_key the application id as [provided by GDS]
13
+ # @param [String] secret_key the application secret as [provided by Bitly]
14
+ def initialize(app, api_key = nil, secret_key = nil, options = {}, &block)
15
+ client_options = {
16
+ :site => "#{GDS::SSO::Config.oauth_root_url}/",
17
+ :authorize_url => "#{GDS::SSO::Config.oauth_root_url}/oauth/authorize",
18
+ :token_url => "#{GDS::SSO::Config.oauth_root_url}/oauth/access_token",
19
+ :access_token_url => "#{GDS::SSO::Config.oauth_root_url}/oauth/access_token"
20
+ }
21
+
22
+ super(app, :gds, api_key, secret_key, client_options, options, &block)
23
+ end
24
+
25
+ protected
26
+
27
+ def fetch_user_data
28
+ @access_token.get('/user.json')
29
+ end
30
+
31
+ def user_hash
32
+ @user_hash ||= MultiJson.decode(fetch_user_data)['user']
33
+ end
34
+
35
+ def build_auth_hash
36
+ {'uid' => user_hash['uid'], 'user_info' => {'name' => user_hash['name'], 'email' => user_hash['email']}, 'extra' => {'user_hash' => user_hash}}
37
+ end
38
+
39
+ def auth_hash
40
+ OmniAuth::Utils.deep_merge(super, build_auth_hash)
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ # Allow you to add authentication request from the router:
4
+ #
5
+ # authenticate(:user) do
6
+ # resources :post
7
+ # end
8
+ #
9
+ # Stolen from devise
10
+ def authenticate(scope)
11
+ constraint = lambda do |request|
12
+ request.env["warden"].authenticate!(:scope => scope)
13
+ end
14
+
15
+ constraints(constraint) do
16
+ yield
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_support/concern'
2
+
3
+ module GDS
4
+ module SSO
5
+ module User
6
+ def self.user_params_from_auth_hash(auth_hash)
7
+ {'uid' => auth_hash['uid'], 'email' => auth_hash['user_info']['email'], 'name' => auth_hash['user_info']['name'], 'version' => auth_hash['extra']['user_hash']['version']}
8
+ end
9
+
10
+ extend ActiveSupport::Concern
11
+
12
+ module ClassMethods
13
+ def find_for_gds_oauth(auth_hash)
14
+ if user = self.find_by_uid(auth_hash["uid"])
15
+ user
16
+ else # Create a new user.
17
+ self.create!(GDS::SSO::User.user_params_from_auth_hash(auth_hash))
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,43 @@
1
+ require 'warden'
2
+ require 'omniauth/oauth'
3
+
4
+ Warden::Manager.serialize_into_session do |user|
5
+ user.uid
6
+ end
7
+
8
+ Warden::Manager.serialize_from_session do |uid|
9
+ GDS::SSO::Config.user_klass.find_by_uid(uid)
10
+ end
11
+
12
+ Warden::Strategies.add(:gds_sso) do
13
+ def valid?
14
+ true
15
+ end
16
+
17
+ def authenticate!
18
+ if request.env['omniauth.auth'].nil?
19
+ fail!("No credentials, bub")
20
+ else
21
+ user = prep_user(request.env['omniauth.auth'])
22
+ success!(user)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def prep_user(auth_hash)
29
+ user = GDS::SSO::Config.user_klass.find_for_gds_oauth(auth_hash)
30
+ fail!("Couldn't process credentials") unless user
31
+ user
32
+ end
33
+ end
34
+
35
+ Warden::Strategies.add(:mock_gds_sso) do
36
+ def valid?
37
+ true
38
+ end
39
+
40
+ def authenticate!
41
+ success!(GDS::SSO::Config.user_klass.first)
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ require 'bundler'
2
+ Bundler.setup :default, :development, :test
3
+
4
+ require 'test/unit'
5
+ require 'mocha'
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+ require 'json'
3
+ require 'gds-sso'
4
+ require 'gds-sso/omniauth_strategy'
5
+
6
+ class TestOmniAuthStrategy < Test::Unit::TestCase
7
+ def setup
8
+ @strategy = OmniAuth::Strategies::Gds.new(:gds, 'client_id', 'client_secret')
9
+ @strategy.stubs(:fetch_user_data).returns({'user' => {'uid' => 'abcde', 'version' => 1, 'name' => 'Matt Patterson', 'email' => 'matt@alphagov.co.uk', 'github' => 'fidothe', 'twitter' => 'fidothe'}}.to_json)
10
+ end
11
+
12
+ def test_basic_auth_hash_structure
13
+ assert_equal 'Matt Patterson', @strategy.send(:build_auth_hash)['user_info']['name']
14
+ assert_equal 'matt@alphagov.co.uk', @strategy.send(:build_auth_hash)['user_info']['email']
15
+ end
16
+
17
+ def test_extra_auth_hash_structure
18
+ expected = {'uid' => 'abcde', 'version' => 1, 'name' => 'Matt Patterson', 'email' => 'matt@alphagov.co.uk', 'github' => 'fidothe', 'twitter' => 'fidothe'}
19
+ assert_equal expected, @strategy.send(:build_auth_hash)['extra']['user_hash']
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+ require 'gds-sso/user'
3
+
4
+ class TestUser < Test::Unit::TestCase
5
+ def setup
6
+ @auth_hash = {
7
+ 'provider' => 'gds',
8
+ 'uid' => 'abcde',
9
+ 'credentials' => {'token' => 'abcdefg', 'secret' => 'abcdefg'},
10
+ 'user_info' => {'name' => 'Matt Patterson', 'email' => 'matt@alphagov.co.uk'},
11
+ 'extra' => {'user_hash' => {'uid' => 'abcde', 'version' => 1, 'name' => 'Matt Patterson', 'email' => 'matt@alphagov.co.uk', 'github' => 'fidothe', 'twitter' => 'fidothe'}}
12
+ }
13
+ end
14
+
15
+ def test_user_params_creation
16
+ expected = {'uid' => 'abcde', 'version' => 1, 'name' => 'Matt Patterson', 'email' => 'matt@alphagov.co.uk'}
17
+ assert_equal expected, GDS::SSO::User.user_params_from_auth_hash(@auth_hash)
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gds-sso
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Patterson
9
+ - James Stewart
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-01 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &70320597236800 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70320597236800
26
+ - !ruby/object:Gem::Dependency
27
+ name: warden
28
+ requirement: &70320597236420 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70320597236420
37
+ - !ruby/object:Gem::Dependency
38
+ name: oa-oauth
39
+ requirement: &70320597236000 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *70320597236000
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: &70320597235500 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 0.9.2
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *70320597235500
59
+ - !ruby/object:Gem::Dependency
60
+ name: mocha
61
+ requirement: &70320597235040 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 0.9.0
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *70320597235040
70
+ description: Client for GDS' OAuth 2-based SSO
71
+ email:
72
+ - matt@constituentparts.com
73
+ - james.stewart@digital.cabinet-office.gov.uk
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - lib/gds-sso/config.rb
79
+ - lib/gds-sso/controller_methods.rb
80
+ - lib/gds-sso/failure_app.rb
81
+ - lib/gds-sso/omniauth_strategy.rb
82
+ - lib/gds-sso/routes.rb
83
+ - lib/gds-sso/user.rb
84
+ - lib/gds-sso/warden_config.rb
85
+ - lib/gds-sso.rb
86
+ - README.md
87
+ - Gemfile
88
+ - Rakefile
89
+ - test/test_helper.rb
90
+ - test/test_omniauth_strategy.rb
91
+ - test/test_user.rb
92
+ homepage: https://github.com/alphagov/gds-sso
93
+ licenses: []
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project: gds-sso
112
+ rubygems_version: 1.8.10
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: Client for GDS' OAuth 2-based SSO
116
+ test_files:
117
+ - test/test_helper.rb
118
+ - test/test_omniauth_strategy.rb
119
+ - test/test_user.rb