Merge branch 'allowed_service_ips'

Conflicts:
	lib/casserver/server.rb
	spec/spec_helper.rb
This commit is contained in:
Tyler Pickett 2012-12-21 11:39:49 -06:00
Родитель 71454813bf 6b1ccf9ae4
Коммит d323c3af93
10 изменённых файлов: 187 добавлений и 67 удалений

Просмотреть файл

@ -532,3 +532,12 @@ log:
# convert this to "jsmith".
#downcase_username: true
# If you'd like to limit the service hosts that can use CAS for authentication,
# add the individual IPs and IP ranges in CIDR notation below. Leaving this
# setting blank will allow any server to authenticate users via the CAS server
# and potentially harvest sensitive user information.
#allowed_service_ips:
# - 127.0.0.1
# - 192.168.0.0/24

Просмотреть файл

@ -29,6 +29,7 @@ GEM
xpath (~> 0.1.4)
childprocess (0.3.5)
ffi (~> 1.0, >= 1.0.6)
crack (0.3.1)
crypt-isaac (0.9.1)
diff-lcs (1.1.3)
ffi (1.1.5)
@ -77,6 +78,9 @@ GEM
sqlite3 (1.3.6)
thor (0.16.0)
tilt (1.3.3)
webmock (1.9.0)
addressable (>= 2.2.7)
crack (>= 0.1.7)
xpath (0.1.4)
nokogiri (~> 1.3)
@ -92,6 +96,7 @@ DEPENDENCIES
guard (~> 1.4.0)
guard-rspec (= 2.0.0)
net-ldap (~> 0.1.1)
nokogiri (~> 1.3)
rack-test
rake (= 0.8.7)
rb-fsevent (~> 0.9.2)
@ -99,3 +104,4 @@ DEPENDENCIES
rspec-core
rubycas-server!
sqlite3 (~> 1.3.1)
webmock (~> 1.8)

Просмотреть файл

@ -39,6 +39,7 @@ GEM
xpath (~> 0.1.4)
childprocess (0.3.5)
ffi (~> 1.0, >= 1.0.6)
crack (0.3.1)
crypt-isaac (0.9.1)
diff-lcs (1.1.3)
ffi (1.1.5)
@ -89,6 +90,9 @@ GEM
thor (0.16.0)
tilt (1.3.3)
tzinfo (0.3.33)
webmock (1.9.0)
addressable (>= 2.2.7)
crack (>= 0.1.7)
xpath (0.1.4)
nokogiri (~> 1.3)
@ -104,6 +108,7 @@ DEPENDENCIES
guard (~> 1.4.0)
guard-rspec (= 2.0.0)
net-ldap (~> 0.1.1)
nokogiri (~> 1.3)
rack-test
rake (= 0.8.7)
rb-fsevent (~> 0.9.2)
@ -111,3 +116,4 @@ DEPENDENCIES
rspec-core
rubycas-server!
sqlite3 (~> 1.3.1)
webmock (~> 1.8)

Просмотреть файл

@ -40,6 +40,7 @@ GEM
xpath (~> 0.1.4)
childprocess (0.3.5)
ffi (~> 1.0, >= 1.0.6)
crack (0.3.1)
crypt-isaac (0.9.1)
diff-lcs (1.1.3)
ffi (1.1.5)
@ -90,6 +91,9 @@ GEM
thor (0.16.0)
tilt (1.3.3)
tzinfo (0.3.33)
webmock (1.9.0)
addressable (>= 2.2.7)
crack (>= 0.1.7)
xpath (0.1.4)
nokogiri (~> 1.3)
@ -105,6 +109,7 @@ DEPENDENCIES
guard (~> 1.4.0)
guard-rspec (= 2.0.0)
net-ldap (~> 0.1.1)
nokogiri (~> 1.3)
rack-test
rake (= 0.8.7)
rb-fsevent (~> 0.9.2)
@ -112,3 +117,4 @@ DEPENDENCIES
rspec-core
rubycas-server!
sqlite3 (~> 1.3.1)
webmock (~> 1.8)

Просмотреть файл

@ -40,6 +40,7 @@ GEM
xpath (~> 0.1.4)
childprocess (0.3.5)
ffi (~> 1.0, >= 1.0.6)
crack (0.3.1)
crypt-isaac (0.9.1)
diff-lcs (1.1.3)
ffi (1.1.5)
@ -90,6 +91,9 @@ GEM
thor (0.16.0)
tilt (1.3.3)
tzinfo (0.3.33)
webmock (1.9.0)
addressable (>= 2.2.7)
crack (>= 0.1.7)
xpath (0.1.4)
nokogiri (~> 1.3)
@ -105,6 +109,7 @@ DEPENDENCIES
guard (~> 1.4.0)
guard-rspec (= 2.0.0)
net-ldap (~> 0.1.1)
nokogiri (~> 1.3)
rack-test
rake (= 0.8.7)
rb-fsevent (~> 0.9.2)
@ -112,3 +117,4 @@ DEPENDENCIES
rspec-core
rubycas-server!
sqlite3 (~> 1.3.1)
webmock (~> 1.8)

Просмотреть файл

@ -1,5 +1,4 @@
require 'active_support'
require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext'
module CASServer

Просмотреть файл

@ -600,58 +600,68 @@ module CASServer
# 2.4
# 2.4.1
get "#{uri_path}/validate" do
CASServer::Utils::log_controller_action(self.class, params)
get "#{uri_path}/validate" do
CASServer::Utils::log_controller_action(self.class, params)
# required
@service = clean_service_url(params['service'])
@ticket = params['ticket']
# optional
@renew = params['renew']
if ip_allowed?(request.ip)
# required
@service = clean_service_url(params['service'])
@ticket = params['ticket']
# optional
@renew = params['renew']
st, @error = validate_service_ticket(@service, @ticket)
@success = st && !@error
st, @error = validate_service_ticket(@service, @ticket)
@success = st && !@error
@username = st.username if @success
@username = st.username if @success
else
@success = false
@error = Error.new(:INVALID_REQUEST, 'The IP address of this service has not been allowed')
end
status response_status_from_error(@error) if @error
render @template_engine, :validate, :layout => false
end
render @template_engine, :validate, :layout => false
end
# 2.5
# 2.5.1
get "#{uri_path}/serviceValidate" do
CASServer::Utils::log_controller_action(self.class, params)
CASServer::Utils::log_controller_action(self.class, params)
# force xml content type
content_type 'text/xml', :charset => 'utf-8'
# required
@service = clean_service_url(params['service'])
@ticket = params['ticket']
# optional
@pgt_url = params['pgtUrl']
@renew = params['renew']
if ip_allowed?(request.ip)
# required
@service = clean_service_url(params['service'])
@ticket = params['ticket']
# optional
@pgt_url = params['pgtUrl']
@renew = params['renew']
st, @error = validate_service_ticket(@service, @ticket)
@success = st && !@error
st, @error = validate_service_ticket(@service, @ticket)
@success = st && !@error
if @success
@username = st.username
if @pgt_url
pgt = generate_proxy_granting_ticket(@pgt_url, st)
@pgtiou = pgt.iou if pgt
if @success
@username = st.username
if @pgt_url
pgt = generate_proxy_granting_ticket(@pgt_url, st)
@pgtiou = pgt.iou if pgt
end
@extra_attributes = st.granted_by_tgt.extra_attributes || {}
end
@extra_attributes = st.granted_by_tgt.extra_attributes || {}
else
@success = false
@error = Error.new(:INVALID_REQUEST, 'The IP address of this service has not been allowed')
end
status response_status_from_error(@error) if @error
render :builder, :proxy_validate
end
render :builder, :proxy_validate
end
# 2.6
@ -663,32 +673,38 @@ module CASServer
# force xml content type
content_type 'text/xml', :charset => 'utf-8'
# required
@service = clean_service_url(params['service'])
@ticket = params['ticket']
# optional
@pgt_url = params['pgtUrl']
@renew = params['renew']
if ip_allowed?(request.ip)
@proxies = []
# required
@service = clean_service_url(params['service'])
@ticket = params['ticket']
# optional
@pgt_url = params['pgtUrl']
@renew = params['renew']
t, @error = validate_proxy_ticket(@service, @ticket)
@success = t && !@error
@proxies = []
@extra_attributes = {}
if @success
@username = t.username
t, @error = validate_proxy_ticket(@service, @ticket)
@success = t && !@error
if t.kind_of? CASServer::Model::ProxyTicket
@proxies << t.granted_by_pgt.service_ticket.service
@extra_attributes = {}
if @success
@username = t.username
if t.kind_of? CASServer::Model::ProxyTicket
@proxies << t.granted_by_pgt.service_ticket.service
end
if @pgt_url
pgt = generate_proxy_granting_ticket(@pgt_url, t)
@pgtiou = pgt.iou if pgt
end
@extra_attributes = t.granted_by_tgt.extra_attributes || {}
end
if @pgt_url
pgt = generate_proxy_granting_ticket(@pgt_url, t)
@pgtiou = pgt.iou if pgt
end
@extra_attributes = t.granted_by_tgt.extra_attributes || {}
else
@success = false
@error = Error.new(:INVALID_REQUEST, 'The IP address of this service has not been allowed')
end
status response_status_from_error(@error) if @error
@ -751,6 +767,14 @@ module CASServer
super engine, data, options, views
end
def ip_allowed?(ip)
require 'ipaddr'
allowed_ips = Array(settings.config[:allowed_service_ips])
allowed_ips.empty? || allowed_ips.any? { |i| IPAddr.new(i) === ip }
end
helpers do
def authenticated?
@authenticated
@ -761,4 +785,4 @@ module CASServer
end
end
end
end
end

Просмотреть файл

@ -141,27 +141,80 @@ describe 'CASServer' do
end
end
describe "proxyValidate" do
describe 'validation' do
let(:allowed_ip) { '127.0.0.1' }
let(:unallowed_ip) { '10.0.0.1' }
let(:service) { @target_service }
before do
load_server("default_config")
load_server('default_config') # 127.0.0.0/24 is allowed here
reset_spec_database
visit "/login?service="+CGI.escape(@target_service)
ticket = get_ticket_for(service)
fill_in 'username', :with => VALID_USERNAME
fill_in 'password', :with => VALID_PASSWORD
click_button 'login-submit'
page.current_url.should =~ /^#{Regexp.escape(@target_service)}\/?\?ticket=ST\-[1-9rA-Z]+/
@ticket = page.current_url.match(/ticket=(.*)$/)[1]
Rack::Request.any_instance.stub(:ip).and_return(request_ip)
get "/#{path}?service=#{CGI.escape(service)}&ticket=#{CGI.escape(ticket)}"
end
it "should have extra attributes in proper format" do
get "/serviceValidate?service=#{CGI.escape(@target_service)}&ticket=#{@ticket}"
subject { last_response }
last_response.content_type.should match 'text/xml'
last_response.body.should match "<test_utf_string>Ютф</test_utf_string>"
describe 'validate' do
let(:path) { 'validate' }
context 'from allowed IP' do
let(:request_ip) { allowed_ip }
it { should be_ok }
its(:body) { should match 'yes' }
end
context 'from unallowed IP' do
let(:request_ip) { unallowed_ip }
its(:status) { should eql 422 }
its(:body) { should match 'no' }
end
end
describe 'serviceValidate' do
let(:path) { 'serviceValidate' }
context 'from allowed IP' do
let(:request_ip) { allowed_ip }
it { should be_ok }
its(:content_type) { should match 'text/xml' }
its(:body) { should match /cas:authenticationSuccess/i }
its(:body) { should match '<test_utf_string>Ютф</test_utf_string>' }
end
context 'from unallowed IP' do
let(:request_ip) { unallowed_ip }
its(:status) { should eql 422 }
its(:content_type) { should match 'text/xml' }
its(:body) { should match /cas:authenticationFailure.*INVALID_REQUEST/i }
end
end
describe 'proxyValidate' do
let(:path) { 'proxyValidate' }
context 'from allowed IP' do
let(:request_ip) { allowed_ip }
it { should be_ok }
its(:content_type) { should match 'text/xml' }
its(:body) { should match /cas:authenticationSuccess/i }
end
context 'from unallowed IP' do
let(:request_ip) { unallowed_ip }
its(:status) { should eql 422 }
its(:content_type) { should match 'text/xml' }
its(:body) { should match /cas:authenticationFailure.*INVALID_REQUEST/i }
end
end
end
end
end

Просмотреть файл

@ -48,3 +48,6 @@ enable_single_sign_out: true
#maximum_session_lifetime: 172800
#downcase_username: true
allowed_service_ips:
- 127.0.0.0/24

Просмотреть файл

@ -109,6 +109,14 @@ def reset_spec_database
ActiveRecord::Migrator.migrate("db/migrate")
end
def get_ticket_for(service, username = 'spec_user', password = 'spec_password')
visit "/login?service=#{CGI.escape(service)}"
fill_in 'username', :with => username
fill_in 'password', :with => password
click_button 'login-submit'
page.current_url.match(/ticket=(.*)$/)[1]
end
def gem_available?(name)
if Gem::Specification.methods.include?(:find_all_by_name)
not Gem::Specification.find_all_by_name(name).empty?