forked from rubycas/rubycas-server
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:gunark/rubycas-server
- Loading branch information
Showing
7 changed files
with
290 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
source "http://rubygems.org" | ||
gemspec | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,23 @@ See https://github.com/gunark/rubycas-server/commits/ | |
|
||
## Installation | ||
|
||
on ubuntu using unicorn: | ||
|
||
git clone [email protected]:seven1240/rubycas-server.git | ||
cd rubycas-server | ||
sudo bundle install | ||
|
||
If it complains mysql connectivity, do this | ||
|
||
apt-get install libmysqlclient16-dev | ||
sudo gem install mysql2 | ||
|
||
copy resources/config.example.yml into /etc/rubycas-server/config.yml, there's way to put the config in other place, yet to document. Change the config to meet your requests. | ||
|
||
You might also want to change config/unicorn.conf | ||
|
||
unicorn -D -c config/unicorn.conf | ||
|
||
For info and detailed installation instructions please see http://code.google.com/p/rubycas-server | ||
|
||
## License | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
require 'casserver/authenticators/base' | ||
|
||
begin | ||
require 'active_resource' | ||
rescue LoadError | ||
require 'rubygems' | ||
begin | ||
gem 'activeresource', '~> 3.0.0' | ||
rescue Gem::LoadError | ||
$stderr.puts | ||
$stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" | ||
$stderr.puts | ||
$stderr.puts "To use the ActiveResource authenticator, you must first install the 'activeresource' gem." | ||
$stderr.puts | ||
$stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" | ||
exit 1 | ||
end | ||
require 'active_resource' | ||
end | ||
|
||
module CASServer | ||
module Authenticators | ||
|
||
module Helpers | ||
class Identity < ActiveResource::Base | ||
|
||
# define method_name accessor | ||
cattr_accessor(:method_name) do | ||
:authenticate # default value | ||
end | ||
|
||
def self.method_type | ||
@@method_type ||= :post | ||
end | ||
|
||
def self.method_type= type | ||
methods = [:get, :post, :put, :delete] | ||
raise ArgumentError, "Method type should be one of #{methods.map { |m| m.to_s.upcase }.join(', ')}" unless methods.include? type.to_sym | ||
@@method_type = type | ||
end | ||
|
||
# Autenticate an identity using the given method | ||
# @param [Hash] credentials | ||
def self.authenticate(credentials = {}) | ||
response = send(method_type, method_name, credentials) | ||
new.from_authentication_data(response) | ||
end | ||
|
||
# Used to load object attributes from the given response | ||
def from_authentication_data response | ||
load_attributes_from_response(response) | ||
end | ||
end | ||
end | ||
|
||
class ActiveResource < Base | ||
|
||
# This is called at server startup. | ||
# Any class-wide initializiation for the authenticator should be done here. | ||
# (e.g. establish database connection). | ||
# You can leave this empty if you don't need to set up anything. | ||
def self.setup(options) | ||
raise AuthenticatorError, 'You must define at least site option' unless options[:site] | ||
# apply options to active resource object | ||
options.each do |method, arg| | ||
Helpers::Identity.send "#{method}=", arg if Helpers::Identity.respond_to? "#{method}=" | ||
end | ||
$LOG.info "ActiveResource configuration loaded" | ||
end | ||
|
||
# Override this to implement your authentication credential validation. | ||
# This is called each time the user tries to log in. The credentials hash | ||
# holds the credentials as entered by the user (generally under :username | ||
# and :password keys; :service and :request are also included by default) | ||
# | ||
# Note that the standard credentials can be read in to instance variables | ||
# by calling #read_standard_credentials. | ||
def validate(credentials) | ||
begin | ||
$LOG.debug("Starting Active Resource authentication") | ||
result = Helpers::Identity.authenticate(credentials.except(:request)) | ||
extract_extra_attributes(result) if result | ||
!!result | ||
rescue ::ActiveResource::ConnectionError => e | ||
$LOG.warn("Error during authentication: #{e}") | ||
false | ||
end | ||
end | ||
|
||
private | ||
|
||
def extract_extra_attributes(resource) | ||
@extra_attributes = {} | ||
$LOG.debug("Parsing extra attributes") | ||
if @options[:extra_attributes] | ||
extra_attributes_to_extract.each do |attr| | ||
@extra_attributes[attr] = resource.send(attr).to_s | ||
end | ||
else | ||
@extra_attributes = resource.attributes | ||
end | ||
# do filtering | ||
extra_attributes_to_filter.each do |attr| | ||
@extra_attributes.delete(attr) | ||
end | ||
end | ||
|
||
# extract attributes to filter from the given configuration | ||
def extra_attributes_to_filter | ||
# default value if not set | ||
return ['password'] unless @options[:filter_attributes] | ||
# parse option value | ||
if @options[:filter_attributes].kind_of? Array | ||
attrs = @options[:filter_attributes] | ||
elsif @options[:filter_attributes].kind_of? String | ||
attrs = @options[:filter_attributes].split(',').collect { |col| col.strip } | ||
else | ||
$LOG.error("Can't figure out attribute list from #{@options[:filter_attributes].inspect}. This must be an Aarray of column names or a comma-separated list.") | ||
attrs = [] | ||
end | ||
attrs | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# encoding: UTF-8 | ||
require File.dirname(__FILE__) + '/../spec_helper' | ||
|
||
require 'casserver/authenticators/active_resource' | ||
|
||
describe CASServer::Authenticators::Helpers::Identity do | ||
|
||
it { should be_an ActiveResource::Base } | ||
|
||
it "class should respond to :authenticate" do | ||
subject.class.should respond_to :authenticate | ||
end | ||
|
||
it "class should have a method_name accessor" do | ||
CASServer::Authenticators::Helpers::Identity.method_name.should == :authenticate | ||
end | ||
|
||
it "class should have a method_name accessor" do | ||
CASServer::Authenticators::Helpers::Identity.method_type.should == :post | ||
end | ||
|
||
it "class method_type accessor should validate type" do | ||
expect { | ||
CASServer::Authenticators::Helpers::Identity.method_type = :foo | ||
}.to raise_error(ArgumentError) | ||
end | ||
|
||
end | ||
|
||
describe CASServer::Authenticators::ActiveResource do | ||
|
||
describe "#setup" do | ||
|
||
it "should configure the identity object" do | ||
CASServer::Authenticators::Helpers::Identity.should_receive(:user=).with('httpuser').once | ||
CASServer::Authenticators::ActiveResource.setup :site => 'http://api.example.org', :user => 'httpuser' | ||
end | ||
|
||
it "should configure the method_type" do | ||
CASServer::Authenticators::Helpers::Identity.should_receive(:method_type=).with('get').once | ||
CASServer::Authenticators::ActiveResource.setup :site => 'http://api.example.org', :method_type => 'get' | ||
end | ||
|
||
it "should raise if site option is missing" do | ||
expect { | ||
CASServer::Authenticators::ActiveResource.setup({}).should | ||
}.to raise_error(CASServer::AuthenticatorError, /site option/) | ||
end | ||
end | ||
|
||
describe "#validate" do | ||
|
||
let(:credentials) { {:username => 'validusername', | ||
:password => 'validpassword', | ||
:service => 'test.service'} } | ||
|
||
let(:auth) { CASServer::Authenticators::ActiveResource.new } | ||
|
||
def mock_authenticate identity = nil | ||
identity = CASServer::Authenticators::Helpers::Identity.new if identity.nil? | ||
CASServer::Authenticators::Helpers::Identity.stub!(:authenticate).and_return(identity) | ||
end | ||
|
||
def sample_identity attrs = {} | ||
identity = CASServer::Authenticators::Helpers::Identity.new | ||
attrs.each { |k,v| identity.send "#{k}=", v } | ||
identity | ||
end | ||
|
||
it "should call Identity#autenticate with the given params" do | ||
CASServer::Authenticators::Helpers::Identity.should_receive(:authenticate).with(credentials).once | ||
auth.validate(credentials) | ||
end | ||
|
||
it "should return identity object attributes as extra attributes" do | ||
auth.configure({}.with_indifferent_access) | ||
identity = sample_identity({:email => '[email protected]'}) | ||
mock_authenticate identity | ||
auth.validate(credentials).should be_true | ||
auth.extra_attributes.should == identity.attributes | ||
end | ||
|
||
it "should return false when http raises" do | ||
CASServer::Authenticators::Helpers::Identity.stub!(:authenticate).and_raise(ActiveResource::ForbiddenAccess.new({})) | ||
auth.validate(credentials).should be_false | ||
end | ||
|
||
it "should apply extra_attribute filter" do | ||
auth.configure({ :extra_attributes => 'age'}.with_indifferent_access) | ||
mock_authenticate sample_identity({ :email => '[email protected]', :age => 28 }) | ||
auth.validate(credentials).should be_true | ||
auth.extra_attributes.should == { "age" => "28" } | ||
end | ||
|
||
it "should only extract not filtered attributes" do | ||
auth.configure({ :filter_attributes => 'age'}.with_indifferent_access) | ||
mock_authenticate sample_identity({ :email => '[email protected]', :age => 28 }) | ||
auth.validate(credentials).should be_true | ||
auth.extra_attributes.should == { "email" => '[email protected]' } | ||
end | ||
|
||
it "should filter password if filter attributes is not given" do | ||
auth.configure({}.with_indifferent_access) | ||
mock_authenticate sample_identity({ :email => '[email protected]', :password => 'secret' }) | ||
auth.validate(credentials).should be_true | ||
auth.extra_attributes.should == { "email" => '[email protected]' } | ||
end | ||
end | ||
end |