contour 0.8.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,19 @@
1
+ == 0.9.0
2
+
3
+ * Enhancements
4
+ * Updated Contour to work with OmniAuth 1.0
5
+ * Contour will now provide support for the following providers:
6
+ * OmniAuth OpenID
7
+ * OmniAuth LDAP
8
+ * OmniAuth Twitter
9
+ * OmniAuth Facebook
10
+ * OmniAuth LinkedIn
11
+ * OmniAuth CAS
12
+ * Authentication provider names now rely on the OmniAuth camelizations
13
+
14
+ * Testing
15
+ * SimpleCov testing updated to show gem test coverage
16
+
1
17
  == 0.8.4
2
18
 
3
19
  * Bug Fix
data/README.rdoc CHANGED
@@ -46,7 +46,7 @@ Make sure you have Rails 3.2.0
46
46
 
47
47
  Modify Gemfile and add
48
48
 
49
- gem 'contour', '~> 0.8.4' # Basic Layout and Assets
49
+ gem 'contour', '~> 0.9.0' # Basic Layout and Assets
50
50
 
51
51
  Run Bundle install
52
52
 
@@ -118,10 +118,10 @@ Add the following to your app/models/user.rb
118
118
  end
119
119
 
120
120
  def apply_omniauth(omniauth)
121
- unless omniauth['user_info'].blank?
122
- self.email = omniauth['user_info']['email'] if email.blank?
123
- self.first_name = omniauth['user_info']['first_name'] if first_name.blank?
124
- self.last_name = omniauth['user_info']['last_name'] if last_name.blank?
121
+ unless omniauth['info'].blank?
122
+ self.email = omniauth['info']['email'] if email.blank?
123
+ self.first_name = omniauth['info']['first_name'] if first_name.blank?
124
+ self.last_name = omniauth['info']['last_name'] if last_name.blank?
125
125
  end
126
126
  authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
127
127
  end
@@ -135,11 +135,7 @@ Add the following to your app/models/authentication.rb
135
135
  belongs_to :user
136
136
 
137
137
  def provider_name
138
- if provider == 'open_id'
139
- "OpenID"
140
- else
141
- provider.titleize
142
- end
138
+ OmniAuth.config.camelizations[provider.to_s.downcase] || provider.to_s.titleize
143
139
  end
144
140
 
145
141
  Edit config/initializers/devise.rb and comment out the sign_out_via delete line
@@ -4,11 +4,11 @@ class Contour::AuthenticationsController < ApplicationController
4
4
  end
5
5
 
6
6
  def passthru
7
- render :file => "#{Rails.root}/public/404", :formats => [:html], :status => 404, :layout => false
7
+ render file: "#{Rails.root}/public/404", formats: [:html], status: 404, layout: false
8
8
  end
9
9
 
10
10
  def failure
11
- redirect_to new_user_session_path, :alert => params[:message].blank? ? nil : params[:message].humanize
11
+ redirect_to new_user_session_path, alert: params[:message].blank? ? nil : params[:message].humanize
12
12
  end
13
13
 
14
14
  def create
@@ -18,10 +18,10 @@ class Contour::AuthenticationsController < ApplicationController
18
18
 
19
19
  if omniauth
20
20
 
21
- omniauth['uid'] = omniauth['user_info']['email'] if omniauth['provider'] == 'google_apps' and omniauth['user_info']
21
+ omniauth['uid'] = omniauth['info']['email'] if omniauth['provider'] == 'google_apps' and omniauth['info']
22
22
  authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
23
23
  logger.info "OMNI AUTH INFO: #{omniauth.inspect}"
24
- omniauth['user_info']['email'] = omniauth['extra']['user_hash']['email'] if omniauth['user_info'] and omniauth['user_info']['email'].blank? and omniauth['extra'] and omniauth['extra']['user_hash']
24
+ omniauth['info']['email'] = omniauth['extra']['raw_info']['email'] if omniauth['info'] and omniauth['info']['email'].blank? and omniauth['extra'] and omniauth['extra']['raw_info']
25
25
  if authentication
26
26
  logger.info "Existing authentication found."
27
27
  session["user_return_to"] = request.env["action_dispatch.request.unsigned_session_cookie"]["user_return_to"] if request.env and request.env["action_dispatch.request.unsigned_session_cookie"] and request.env["action_dispatch.request.unsigned_session_cookie"]["user_return_to"] and session["user_return_to"].blank?
@@ -29,8 +29,8 @@ class Contour::AuthenticationsController < ApplicationController
29
29
  sign_in_and_redirect(:user, authentication.user)
30
30
  elsif current_user
31
31
  logger.info "Logged in user found, creating associated authentication."
32
- current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
33
- redirect_to authentications_path, :notice => "Authentication successful."
32
+ current_user.authentications.create!( provider: omniauth['provider'], uid: omniauth['uid'] )
33
+ redirect_to authentications_path, notice: "Authentication successful."
34
34
  else
35
35
  logger.info "Creating new user with new authentication."
36
36
  user = User.new(params[:user])
@@ -49,7 +49,7 @@ class Contour::AuthenticationsController < ApplicationController
49
49
  request.env.keys.each do |key|
50
50
  logger.info "request.env[#{key}]: #{request.env[key]}"
51
51
  end
52
- redirect_to authentications_path, :alert => "Authentication not successful."
52
+ redirect_to authentications_path, alert: "Authentication not successful."
53
53
  end
54
54
 
55
55
  end
@@ -2,10 +2,6 @@ class Authentication < ActiveRecord::Base
2
2
  belongs_to :user
3
3
 
4
4
  def provider_name
5
- if provider == 'open_id'
6
- "OpenID"
7
- else
8
- provider.to_s.titleize
9
- end
5
+ OmniAuth.config.camelizations[provider.to_s.downcase] || provider.to_s.titleize
10
6
  end
11
7
  end
@@ -2,21 +2,10 @@
2
2
  <tr>
3
3
  <% PROVIDERS.each do |provider| %>
4
4
  <% url_params = (provider == :google_apps) ? '?domain=gmail.com' : '' %>
5
- <% if provider == :open_id %>
6
- <% provider_name = 'OpenID' %>
7
- <% image_name = 'openid' %>
8
- <% elsif provider == :linked_in %>
9
- <% provider_name = 'LinkedIn' %>
10
- <% image_name = 'linkedin' %>
11
- <% elsif [:LDAP, :CAS].include?(provider) %>
12
- <% provider_name = provider.to_s.upcase %>
13
- <% image_name = provider.to_s.downcase %>
14
- <% else %>
15
- <% provider_name = provider.to_s.titleize %>
16
- <% image_name = provider.to_s.downcase %>
17
- <% end %>
5
+ <% provider_name = OmniAuth.config.camelizations[provider.to_s.downcase] || provider.to_s.titleize %>
6
+ <% image_name = provider.to_s.downcase %>
18
7
  <td style="white-space:nowrap;text-align:center;border-width:0px">
19
- <a href="<%= request.script_name %><%= "/" + OmniAuth.config.path_prefix.split('/').last.to_s %><%= "/" + provider.to_s.downcase %><%= url_params %>" class="noicon"><%= image_tag "contour/#{image_name}_64.png", :align => 'absmiddle', :height => "64px", :alt => provider_name %></a>
8
+ <a href="<%= request.script_name %><%= "/" + OmniAuth.config.path_prefix.split('/').last.to_s %><%= "/" + provider.to_s.downcase %><%= url_params %>" class="noicon"><%= image_tag "contour/#{image_name}_64.png", align: 'absmiddle', height: "64px", alt: provider_name %></a>
20
9
  <br /><br />
21
10
  <%= provider_name %>
22
11
  </td>
@@ -1,21 +1,10 @@
1
1
  <% PROVIDERS.each do |provider| %>
2
2
  <% url_params = (provider == :google_apps) ? '?domain=gmail.com' : '' %>
3
- <% if provider == :open_id %>
4
- <% provider_name = 'OpenID' %>
5
- <% image_name = 'openid' %>
6
- <% elsif provider == :linked_in %>
7
- <% provider_name = 'LinkedIn' %>
8
- <% image_name = 'linkedin' %>
9
- <% elsif [:LDAP, :CAS].include?(provider) %>
10
- <% provider_name = provider.to_s.upcase %>
11
- <% image_name = provider.to_s.downcase %>
12
- <% else %>
13
- <% provider_name = provider.to_s.titleize %>
14
- <% image_name = provider.to_s.downcase %>
15
- <% end %>
16
- <li class="parent li_right" style="top:-4px;height:20px;"> <%# margin-right:15px;padding-left:0px;padding-right:0px %>
3
+ <% provider_name = OmniAuth.config.camelizations[provider.to_s.downcase] || provider.to_s.titleize %>
4
+ <% image_name = provider.to_s.downcase %>
5
+ <li class="parent li_right" style="top:-4px;height:20px;">
17
6
  <a href="<%= request.script_name %><%= "/" + OmniAuth.config.path_prefix.split('/').last.to_s %><%= "/" + provider.to_s.downcase %><%= url_params %>" class="noicon">
18
- <%= image_tag "contour/#{image_name}_32.png", :align => 'absmiddle', :height => "28px", :title => provider_name %>
7
+ <%= image_tag "contour/#{image_name}_32.png", align: 'absmiddle', height: "28px", title: provider_name %>
19
8
  </a>
20
9
  </li>
21
10
  <% end %>
data/contour.gemspec CHANGED
@@ -20,10 +20,16 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.platform = Gem::Platform::RUBY
22
22
 
23
- s.add_dependency 'rails', '~> 3.2.0.rc2'
24
- s.add_dependency 'devise', '~> 1.4.9'
25
- s.add_dependency 'omniauth', '=0.2.6'
26
- s.add_dependency 'jquery-rails', '~> 2.0.0'
23
+ s.add_dependency 'rails', '~> 3.2.0.rc2'
24
+ s.add_dependency 'jquery-rails', '~> 2.0.0'
25
+ s.add_dependency 'devise', '~> 1.5.3'
26
+ s.add_dependency 'omniauth', '~> 1.0.1'
27
+ s.add_dependency 'omniauth-openid', '~> 1.0.1'
28
+ s.add_dependency 'omniauth-ldap', '~> 1.0.2'
29
+ s.add_dependency 'omniauth-twitter', '~> 0.0.7'
30
+ s.add_dependency 'omniauth-facebook', '~> 1.2.0'
31
+ s.add_dependency 'omniauth-linkedin', '~> 0.0.6'
32
+ s.add_dependency 'omniauth-cas', '~> 0.0.3'
27
33
 
28
34
  s.files = Dir["{app,config,db,lib}/**/*"] + ["CHANGELOG.rdoc", "contour.gemspec", "LICENSE", "Rakefile", "README.rdoc"]
29
35
  s.test_files = Dir["test/**/*"]
@@ -1,4 +1,5 @@
1
1
  require 'omniauth'
2
+ require 'omniauth-ldap'
2
3
 
3
4
  # Overwriting methods from Rack
4
5
  module OmniAuth
@@ -13,11 +14,11 @@ module OmniAuth
13
14
  def on_failure(&block)
14
15
  OmniAuth.config.on_failure = block
15
16
  end
16
-
17
+
17
18
  def configure(&block)
18
19
  OmniAuth.configure(&block)
19
20
  end
20
-
21
+
21
22
  def provider(klass, *args, &block)
22
23
  if klass.is_a?(Class)
23
24
  middleware = klass
@@ -28,114 +29,89 @@ module OmniAuth
28
29
  raise LoadError, "Could not find matching strategy for #{klass.inspect}. You may need to install an additional gem (such as omniauth-#{klass})."
29
30
  end
30
31
  end
31
-
32
+
32
33
  use middleware, *args, &block
33
34
  end
34
-
35
+
35
36
  def call(env)
36
37
  to_app.call(env)
37
38
  end
38
39
  end
40
+ end
39
41
 
40
- module Strategies
41
- class LDAP
42
- # include OmniAuth::Strategy
42
+ # Fix for LDAP Authentication with Domains
43
+ module OmniAuth
44
+ class Form
45
+ def hidden_field(name, value)
46
+ @html << "<input type='hidden' id='#{name}' name='#{name}' value='#{value}'/>"
47
+ self
48
+ end
49
+ end
50
+
51
+ module LDAP
52
+ class Adaptor
53
+ class LdapError < StandardError; end
54
+ class ConfigurationError < StandardError; end
55
+ class AuthenticationError < StandardError; end
56
+ class ConnectionError < StandardError; end
57
+
58
+ attr_accessor :bind_dn, :password
59
+ attr_reader :connection, :uid, :base, :auth
60
+ def self.validate(configuration={})
61
+ message = []
62
+ MUST_HAVE_KEYS.each do |name|
63
+ message << name if configuration[name].nil?
64
+ end
65
+ raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
66
+ end
43
67
 
44
- def initialize(app, options = {}, &block)
45
- # Rails.logger.debug "Contour::Fixes => Omniauth::Strategies::LDAP::initialize"
46
- super(app, options[:name] || :ldap, options.dup, &block)
47
- @name_proc = (@options.delete(:name_proc) || Proc.new {|name| name})
48
- @adaptor = OmniAuth::Strategies::LDAP::Adaptor.new(options)
49
- @domain = options[:domain] # Added
68
+ def bind_as(args = {})
69
+ result = false
70
+ ldap = @connection
71
+ ldap.auth args[:username], args[:password]
72
+ rs = ldap.search(base: args[:base], filter: Net::LDAP::Filter.eq(@uid, args[:username].split('\\').last.to_s))
73
+ result = rs.first if rs
74
+ result
50
75
  end
51
76
 
52
- protected
77
+ end
78
+ end
79
+
80
+ module Strategies
81
+ class LDAP
82
+ include OmniAuth::Strategy
83
+
84
+ option :domain, ''
53
85
 
54
- # Reroutes directly to callback_phase instead of redirecting to callback_path
55
- # TODO: Possibility that this could be removed in the future.
56
86
  def request_phase
57
- # Rails.logger.info "Contour::Fixes => Omniauth::Strategies::LDAP::request_phase Request Phase Hook"
58
- if env['REQUEST_METHOD'] == 'GET'
59
- get_credentials
60
- else
61
- session['omniauth.ldap'] = {'username' => request['username'], 'password' => request['password']}
62
- if true
63
- # Rails.logger.info "Contour::Fixes => Omniauth::Strategies::LDAP::request_phase Skipping Redirect"
64
- callback_phase # Added
65
- else
66
- redirect callback_path
67
- end
68
- end
87
+ OmniAuth::LDAP::Adaptor.validate @options
88
+ f = OmniAuth::Form.new(:title => (options[:title] || "LDAP Authentication"), :url => "#{@env['SCRIPT_NAME']}" + callback_path)
89
+ f.hidden_field 'domain', options[:domain] # Added to allow domains from option
90
+ f.text_field 'Login', 'username'
91
+ f.password_field 'Password', 'password'
92
+ f.to_response
69
93
  end
70
94
 
71
- # Includes addition of a "domain"
72
95
  def callback_phase
73
- failure_temp_path = "#{@env['SCRIPT_NAME']}/#{OmniAuth.config.path_prefix.split('/').last}/failure?message=invalid_credentials"
74
-
96
+ failure_temp_path = "#{@env['SCRIPT_NAME']}/#{OmniAuth.config.path_prefix.split('/').last}/failure?message=invalid_credentials" # Added
97
+ bind_dn = "#{request['domain'] + '\\' unless request['domain'].blank?}#{request['username']}" # Added
98
+ @options[:bind_dn] = bind_dn if @options[:bind_dn].blank? # Added
99
+ @adaptor = OmniAuth::LDAP::Adaptor.new @options
100
+
101
+ raise MissingCredentialsError.new("Missing login credentials") if request['username'].nil? || request['password'].nil?
75
102
  begin
76
- creds = session['omniauth.ldap']
77
- session.delete 'omniauth.ldap'
78
- @ldap_user_info = {}
79
- begin
80
- creds['username'] = @domain.to_s + '\\' + creds['username'] unless @domain.blank?
81
- @adaptor.bind(:bind_dn => creds['username'], :password => creds['password'])
82
- rescue Exception => e
83
- Rails.logger.info "Failed to bind with the default credentials: " + e.message
84
- return redirect failure_temp_path
85
- # return fail!(:invalid_credentials, e)
86
- end
87
-
88
- @ldap_user_info = @adaptor.search(:base => @adaptor.base, :filter => Net::LDAP::Filter.eq(@adaptor.uid, creds['username'].split('\\').last.to_s),:limit => 1)
89
- @ldap_user_info.each do |key, val|
90
- @ldap_user_info[key] = val.first if val.class == Array and val.size == 1
91
- end
92
-
93
- @user_info = self.class.map_user(@@config, @ldap_user_info)
103
+ @ldap_user_info = @adaptor.bind_as(base: @adaptor.base, username: bind_dn, :password => request['password']) # Modified
104
+
105
+ # return fail!(:invalid_credentials) if !@ldap_user_info
106
+ return redirect failure_temp_path if !@ldap_user_info
94
107
 
95
- @env['REQUEST_METHOD'] = 'GET'
96
- # @env['PATH_INFO'] = callback_path
97
- @env['PATH_INFO'] = "#{OmniAuth.config.path_prefix.split('/').last}/#{name}/callback"
98
-
99
- @env['omniauth.auth'] = {'provider' => 'ldap', 'uid' => @user_info['uid'], 'user_info' => @user_info, 'bla' => 5}
108
+ @user_info = self.class.map_user(@@config, @ldap_user_info)
109
+ super
100
110
  rescue Exception => e
101
- Rails.logger.info "Exception in callback_phase: #{e.inspect}"
111
+ # return fail!(:ldap_error, e)
102
112
  return redirect failure_temp_path
103
- # return fail!(:invalid_credentials, e)
104
113
  end
105
-
106
- call_app!
107
114
  end
108
-
109
- def self.map_user mapper, object
110
- user = {}
111
- mapper.each do |key, value|
112
- case value
113
- when String
114
- if object[value.downcase.to_sym]
115
- if object[value.downcase.to_sym].kind_of?(Array)
116
- user[key] = object[value.downcase.to_sym].join(', ')
117
- else
118
- user[key] = object[value.downcase.to_sym].to_s
119
- end
120
- end
121
- when Array
122
- # value.each {|v| (user[key] = object[v.downcase.to_sym].to_s; break;) if object[v.downcase.to_sym]}
123
- value.each {|v| (user[key] = object[v.downcase.to_sym].join(', '); break;) if object[v.downcase.to_sym]}
124
- when Hash
125
- value.map do |key1, value1|
126
- pattern = key1.dup
127
- value1.each_with_index do |v,i|
128
- part = '';
129
- v.each {|v1| (part = object[v1.downcase.to_sym].to_s; break;) if object[v1.downcase.to_sym]}
130
- pattern.gsub!("%#{i}",part||'')
131
- end
132
- user[key] = pattern
133
- end
134
- end
135
- end
136
- user
137
- end
138
-
139
115
  end
140
116
  end
141
117
  end
@@ -1,8 +1,8 @@
1
1
  module Contour
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 8
5
- TINY = 4
4
+ MINOR = 9
5
+ TINY = 0
6
6
  BUILD = nil # nil, "pre", "rc", "rc2"
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, BUILD].compact.join('.')
@@ -30,10 +30,10 @@ class User < ActiveRecord::Base
30
30
  end
31
31
 
32
32
  def apply_omniauth(omniauth)
33
- unless omniauth['user_info'].blank?
34
- self.email = omniauth['user_info']['email'] if email.blank?
35
- self.first_name = omniauth['user_info']['first_name'] if first_name.blank?
36
- self.last_name = omniauth['user_info']['last_name'] if last_name.blank?
33
+ unless omniauth['info'].blank?
34
+ self.email = omniauth['info']['email'] if email.blank?
35
+ self.first_name = omniauth['info']['first_name'] if first_name.blank?
36
+ self.last_name = omniauth['info']['last_name'] if last_name.blank?
37
37
  end
38
38
  authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
39
39
  end
@@ -1,22 +1,33 @@
1
+ require 'omniauth-openid'
2
+ require 'omniauth-ldap'
3
+ require 'omniauth-twitter'
4
+ require 'omniauth-facebook'
5
+ require 'omniauth-linkedin'
6
+ require 'omniauth-cas'
7
+
1
8
  require 'openid/store/filesystem'
2
9
  Rails.application.config.middleware.use OmniAuth::Builder do
3
- # provider :open_id, OpenID::Store::Filesystem.new('/tmp'), :name => 'google_apps', :identifier => 'https://www.google.com/accounts/o8/id'
4
- # provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
5
- # provider :facebook, 'APP_ID', 'APP_SECRET'
6
- # provider :linked_in, 'CONSUMER_KEY', 'CONSUMER_SECRET'
7
- # provider :open_id, OpenID::Store::Filesystem.new('/tmp')
8
- # provider :CAS, :cas_server => 'https://www.example.com/cas'
9
- # provider :LDAP, :host => 'ldap.example.com', :port => 389, :method => :simple, :base => 'cn=users,dc=example,dc=com', :uid => 'sAMAccountName', :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO", :domain => ''
10
+ provider :open_id, store: OpenID::Store::Filesystem.new('/tmp'), name: 'google_apps', identifier: 'https://www.google.com/accounts/o8/id'
11
+ provider :open_id, store: OpenID::Store::Filesystem.new('/tmp')
12
+ provider :LDAP, host: 'ldap.example.com', port: 389, method: :plain, base: 'cn=users,dc=example,dc=com', uid: 'sAMAccountName', domain: '' #, try_sasl: true, sasl_mechanisms: "GSS-SPNEGO"
13
+
14
+ provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
15
+ provider :facebook, 'APP_ID', 'APP_SECRET'
16
+ provider :linked_in, 'CONSUMER_KEY', 'CONSUMER_SECRET'
17
+ provider :cas, host: 'cas.yourdomain.com', login_url: 'https://www.example.com/cas'
18
+
19
+ # Other providers will need gems added in your Gemfile, and the provider configured in this block.
20
+ # https://github.com/intridea/omniauth/wiki/List-of-Strategies
10
21
  end
11
22
 
12
23
  # This list will show the first choice as the default, and the rest as potential secondary login methods.
13
- PROVIDERS = [:google_apps, :twitter, :facebook, :linked_in, :open_id, :CAS, :LDAP]
24
+ PROVIDERS = [:google_apps, :open_id, :LDAP, :twitter, :facebook, :linked_in, :cas]
14
25
 
15
26
  # LDAP
16
27
  # :port (required) - The LDAP server port.
17
28
  # :method (required) - May be :plain, :ssl, or :tls.
18
29
  # :base (required) - The distinguished name (DN) for your organization; all users should be searchable under this base.
19
30
  # :uid (required) - The LDAP attribute name for the user name in the login form. Typically AD would be 'sAMAccountName' or 'UniquePersonalIdentifier', while OpenLDAP is 'uid'. You can also use 'dn' for the user to put in the dn in the login form (but usually is too long for user to remember or know).
31
+ # :domain - Enter to keep users from having to enter the LDAP domain, usually in the form domain\username. Backslash will be appended automatically.
20
32
  # :try_sasl - Try to use SASL connection to server.
21
- # :sasl_mechanisms - Mechanisms supported are 'DIGEST-MD5' and 'GSS-SPNEGO'
22
- # :domain - Enter to keep users from having to enter the LDAP domain, usually in the form domain\username. Backslash will be appended automatically.
33
+ # :sasl_mechanisms - Mechanisms supported are 'DIGEST-MD5' and 'GSS-SPNEGO'