Skip to content

Commit 028605c

Browse files
author
Christian Rodriguez
committed
first try porting from CAS omniauth to SAML
1 parent 599d885 commit 028605c

File tree

12 files changed

+225
-58
lines changed

12 files changed

+225
-58
lines changed

Gemfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
gem 'omniauth', '>= 1.1.1'
2-
gem 'omniauth-cas', '>= 1.0.1'
2+
gem 'omniauth-saml', '~> 1.0.0'

TODO

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
arreglar los settings (ahora se hardocdean)
2+
logout
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<% if Redmine::OmniAuthSAML.enabled? %>
2+
3+
<% content_for :header_tags do %>
4+
<%= stylesheet_link_tag "login", :plugin => "redmine_omniauth_saml" %>
5+
<% end %>
6+
7+
<div id="saml-login">
8+
<%= link_to label_for_saml_login, :controller => "account", :action => "login_with_saml_redirect", :provider => "saml", :origin => back_url %>
9+
</div>
10+
11+
<% end %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<% flash.now[:error] = "#{l("label_cas_server")} #{l("activerecord.errors.messages.blank")}" if @settings['cas_server'].blank? %>
2+
3+
<p>
4+
<label><%= l(:label_cas_enabled) %></label>
5+
<%= check_box_tag 'settings[enabled]', true, @settings['enabled'] %>
6+
</p>
7+
<p>
8+
<label><%= l(:label_cas_server) %><span class="required"> *</span></label>
9+
<%= text_field_tag 'settings[cas_server]', @settings['cas_server'], :size => 50 %>
10+
<br />
11+
<em><%= l(:label_example) %>: https://cas.example.com</em>
12+
</p>
13+
<p>
14+
<label><%= l(:label_cas_service_validate_url) %></label>
15+
<%= text_field_tag 'settings[cas_service_validate_url]', @settings['cas_service_validate_url'], :size => 50 %>
16+
<br />
17+
<em><%= l(:label_example) %>: https://cas.example.net/serviceValidate</em>
18+
</p>
19+
<p>
20+
<label><%= l(:label_login_page_text) %></label>
21+
<%= text_field_tag 'settings[label_login_with_cas]', @settings['label_login_with_cas'], :size => 50 %>
22+
</p>
23+
<p>
24+
<label><%= l(:label_replace_redmine_login) %></label>
25+
<%= check_box_tag 'settings[replace_redmine_login]', true, @settings['replace_redmine_login'] %>
26+
</p>

assets/stylesheets/login.css

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#cas-login { margin:3em auto 0; font-size:16px; font-weight:bold; text-align:center; }
2-
#cas-login a, #cas-login a:hover {
1+
#saml-login { margin:3em auto 0; font-size:16px; font-weight:bold; text-align:center; }
2+
#saml-login a, #saml-login a:hover {
33
display:inline-block; padding:0.4em 1em; max-width:800px; text-align:center;
44
color:#fff; background-color:#78cc01; text-decoration:none !important; text-shadow:1px 1px 1px #666;
55
-moz-border-radius:20px; -webkit-border-radius:20px; border-radius:20px;
66
}
7-
#cas-login a:hover { background-color:#70c600; }
8-
#cas-login a:active { position:relative; top:1px; }
7+
#saml-login a:hover { background-color:#70c600; }
8+
#saml-login a:active { position:relative; top:1px; }
99
#login-form table { margin-top:2em; }

config/locales/en.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ en:
66
label_cas_service_validate_url: CAS validation URL (if different)
77
label_replace_redmine_login: Replace Redmine login page
88
text_full_logout_proposal: You may want to %{value} before trying an other username.
9-
text_logout_from_cas: logout from CAS
10-
error_cas_no_ticket: No CAS ticket was found during callback.
11-
error_cas_invalid_ticket: An invalid CAS ticket was specified, it may have expired. Please try authenticating in again.
12-
error_cas_unknown: An unknown error occurred during CAS callback processing.
9+
text_logout_from_saml: logout from SAML
10+
error_saml_no_ticket: No CAS ticket was found during callback.
11+
error_saml_invalid_ticket: An invalid CAS ticket was specified, it may have expired. Please try authenticating in again.
12+
error_saml_unknown: An unknown error occurred during CAS callback processing.

config/routes.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
RedmineApp::Application.routes.draw do
2-
match 'auth/failure', :to => 'account#login_with_cas_failure'
3-
match 'auth/:provider/callback', :to => 'account#login_with_cas_callback'
4-
match 'auth/:provider', :to => 'account#login_with_cas_redirect'
2+
match 'auth/failure', :to => 'account#login_with_saml_failure'
3+
match 'auth/:provider/callback', :to => 'account#login_with_saml_callback'
4+
match 'auth/:provider', :to => 'account#login_with_saml_redirect'
55
end

init.rb

+14-46
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,23 @@
11
require 'redmine'
2-
require 'redmine_omniauth_cas'
3-
require 'redmine_omniauth_cas/hooks'
4-
require 'omniauth/patches'
5-
require 'omniauth/dynamic_full_host'
2+
require 'redmine_omniauth_saml'
3+
require 'redmine_omniauth_saml/hooks'
64

75
# Patches to existing classes/modules
86
ActionDispatch::Callbacks.to_prepare do
9-
require_dependency 'redmine_omniauth_cas/account_helper_patch'
10-
require_dependency 'redmine_omniauth_cas/account_controller_patch'
7+
require_dependency 'redmine_omniauth_saml/account_helper_patch'
8+
require_dependency 'redmine_omniauth_saml/account_controller_patch'
119
end
1210

1311
# Plugin generic informations
14-
Redmine::Plugin.register :redmine_omniauth_cas do
15-
name 'Redmine Omniauth plugin'
16-
description 'This plugin adds Omniauth support to Redmine'
17-
author 'Jean-Baptiste BARTH'
18-
author_url 'mailto:[email protected]'
19-
url 'https://github.com/jbbarth/redmine_omniauth_cas'
20-
version '0.1.2'
21-
requires_redmine :version_or_higher => '2.0.0'
22-
settings :default => { 'enabled' => 'true', 'label_login_with_cas' => '', 'cas_server' => '' },
23-
:partial => 'settings/omniauth_cas_settings'
12+
Redmine::Plugin.register :redmine_omniauth_saml do
13+
name 'Redmine Omniauth SAML plugin'
14+
description 'This plugin adds Omniauth SAML support to Redmine. Based in Omniauth CAS plugin'
15+
author 'Christian A. Rodriguez'
16+
author_url 'mailto:[email protected]'
17+
url 'https://github.com/chrodriguez/redmine_omniauth_saml'
18+
version '0.0.1'
19+
requires_redmine :version_or_higher => '2.3.0'
20+
settings :default => { 'enabled' => 'true', 'label_login_with_saml' => '' },
21+
:partial => 'settings/omniauth_saml_settings'
2422
end
2523

26-
# OmniAuth CAS
27-
setup_app = Proc.new do |env|
28-
addr = Redmine::OmniAuthCAS.cas_server
29-
cas_server = URI.parse(addr)
30-
if cas_server
31-
env['omniauth.strategy'].options.merge! :host => cas_server.host,
32-
:port => cas_server.port,
33-
:path => (cas_server.path != "/" ? cas_server.path : nil),
34-
:ssl => cas_server.scheme == "https"
35-
end
36-
validate = Redmine::OmniAuthCAS.cas_service_validate_url
37-
if validate
38-
env['omniauth.strategy'].options.merge! :service_validate_url => validate
39-
end
40-
# Dirty, not happy with it, but as long as I can't reproduce the bug
41-
# users are blocked because of failing OpenSSL checks, while the cert
42-
# is actually good, so...
43-
# TODO: try to understand why cert verification fails
44-
# Maybe https://github.com/intridea/omniauth/issues/404 can help
45-
env['omniauth.strategy'].options.merge! :disable_ssl_verification => true
46-
end
47-
48-
# tell Rails we use this middleware, with some default value just in case
49-
Rails.application.config.middleware.use OmniAuth::Builder do
50-
#url = "http://nadine.application.ac.centre-serveur.i2/"
51-
use OmniAuth::Strategies::CAS, :host => "localhost",
52-
:port => "9292",
53-
:ssl => false,
54-
:setup => setup_app
55-
end

lib/redmine_omniauth_saml.rb

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module Redmine::OmniAuthSAML
2+
class << self
3+
def settings_hash
4+
# Setting["plugin_redmine_omniauth_cas"]
5+
{
6+
'enabled' => true,
7+
'replace_redmine_login' => true
8+
}
9+
end
10+
11+
def enabled?
12+
settings_hash["enabled"]
13+
end
14+
15+
def saml_server
16+
settings_hash["saml_server"]
17+
end
18+
19+
def saml_logout_url
20+
settings['saml_logout_url']
21+
end
22+
23+
def cas_service_validate_url
24+
settings_hash["cas_service_validate_url"].presence || nil
25+
end
26+
27+
def label_login_with_saml
28+
settings_hash["label_login_with_saml"]
29+
end
30+
end
31+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
require_dependency 'account_controller'
2+
3+
module Redmine::OmniAuthSAML
4+
module AccountControllerPatch
5+
def self.included(base)
6+
base.send(:include, InstanceMethods)
7+
base.class_eval do
8+
unloadable
9+
alias_method_chain :login, :saml
10+
alias_method_chain :logout, :saml
11+
end
12+
end
13+
14+
module InstanceMethods
15+
16+
def login_with_saml
17+
#TODO: test 'replace_redmine_login' feature
18+
if saml_settings["enabled"] && saml_settings["replace_redmine_login"]
19+
redirect_to :controller => "account", :action => "login_with_saml_redirect", :provider => "saml", :origin => back_url
20+
else
21+
login_without_saml
22+
end
23+
end
24+
25+
def login_with_saml_redirect
26+
render :text => "Not Found", :status => 404
27+
end
28+
29+
def login_with_saml_callback
30+
auth = request.env["omniauth.auth"]
31+
#user = User.find_by_provider_and_uid(auth["provider"], auth["uid"])
32+
user = User.find_by_login(auth["uid"]) || User.find_by_mail(auth["uid"])
33+
34+
# taken from original AccountController
35+
# maybe it should be splitted in core
36+
if user.blank?
37+
logger.warn "Failed login for '#{auth[:uid]}' from #{request.remote_ip} at #{Time.now.utc}"
38+
error = l(:notice_account_invalid_creditentials).sub(/\.$/, '')
39+
if saml_settings["enabled"]
40+
link = self.class.helpers.link_to(l(:text_logout_from_saml), saml_logout_url, :target => "_blank")
41+
error << ". #{l(:text_full_logout_proposal, :value => link)}"
42+
end
43+
if saml_settings["replace_redmine_login"]
44+
render_error({:message => error.html_safe, :status => 403})
45+
return false
46+
else
47+
flash[:error] = error
48+
redirect_to signin_url
49+
end
50+
else
51+
user.update_attribute(:last_login_on, Time.now)
52+
params[:back_url] = request.env["omniauth.origin"] unless request.env["omniauth.origin"].blank?
53+
successful_authentication(user)
54+
#cannot be set earlier, because sucessful_authentication() triggers reset_session()
55+
session[:logged_in_with_saml] = true
56+
end
57+
end
58+
59+
def login_with_saml_failure
60+
error = params[:message] || 'unknown'
61+
error = 'error_saml_' + error
62+
if saml_settings["replace_redmine_login"]
63+
render_error({:message => error.to_sym, :status => 500})
64+
return false
65+
else
66+
flash[:error] = l(error.to_sym)
67+
redirect_to signin_url
68+
end
69+
end
70+
71+
def logout_with_saml
72+
if saml_settings["enabled"] && session[:logged_in_with_saml]
73+
logout_user
74+
redirect_to saml_logout_url(home_url)
75+
else
76+
logout_without_saml
77+
end
78+
end
79+
80+
private
81+
def saml_settings
82+
Redmine::OmniAuthSAML.settings_hash
83+
end
84+
85+
def saml_logout_url(service = nil)
86+
return ''
87+
logout_uri = saml_settings['saml_logout_url']
88+
if !service.blank?
89+
logout_uri += service
90+
end
91+
logout_uri
92+
end
93+
94+
end
95+
end
96+
end
97+
98+
unless AccountController.included_modules.include? Redmine::OmniAuthSAML::AccountControllerPatch
99+
AccountController.send(:include, Redmine::OmniAuthSAML::AccountControllerPatch)
100+
AccountController.skip_before_filter :verify_authenticity_token, :only => [:login_with_saml_callback]
101+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require_dependency 'account_helper'
2+
3+
module Redmine::OmniAuthSAML
4+
module AccountHelperPatch
5+
def self.included(base)
6+
base.send(:include, InstanceMethods)
7+
base.class_eval do
8+
unloadable
9+
end
10+
end
11+
12+
module InstanceMethods
13+
def label_for_saml_login
14+
Redmine::OmniAuthCAS.label_login_with_saml.presence || l(:label_login_with_saml)
15+
end
16+
end
17+
end
18+
end
19+
20+
unless AccountHelper.included_modules.include? Redmine::OmniAuthSAML::AccountHelperPatch
21+
AccountHelper.send(:include, Redmine::OmniAuthSAML::AccountHelperPatch)
22+
end

lib/redmine_omniauth_saml/hooks.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Redmine::OmniAuthSAML
2+
class Hooks < Redmine::Hook::ViewListener
3+
render_on :view_account_login_top, :partial => 'redmine_omniauth_saml/view_account_login_top'
4+
end
5+
end

0 commit comments

Comments
 (0)