cassiopeia 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,8 @@
1
+ Cassiopeia
2
+ ===================
3
+
4
+ Yet another custom CAS client/server implementation
5
+
6
+ ===================
7
+
8
+ Copyright (c) 2010 Waveaccess LLC
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ desc 'Default: .'
7
+ task :default => :test
8
+
9
+ PKG_FILES = FileList[ '[a-zA-Z]*', 'lib/**/*' ]
10
+
11
+ spec = Gem::Specification.new do |s|
12
+ s.name = "cassiopeia"
13
+ s.version = "0.0.1"
14
+ s.author = "smecsia"
15
+ s.email = "smecsia@gmail.com"
16
+ #s.homepage = ""
17
+ s.platform = Gem::Platform::RUBY
18
+ s.summary = "Rails plugin for custom CAS(Cassiopeia) server/client implementation"
19
+ s.add_dependency('uuidtools')
20
+ s.add_dependency('rails', '>=2.3.5')
21
+ s.files = PKG_FILES.to_a
22
+ s.require_path = "lib"
23
+ s.has_rdoc = false
24
+ s.extra_rdoc_files = ["README"]
25
+ end
26
+
27
+ desc 'Turn this plugin into a gem.'
28
+ Rake::GemPackageTask.new(spec) do |pkg|
29
+ pkg.gem_spec = spec
30
+ end
31
+
@@ -0,0 +1,86 @@
1
+ module Cassiopeia
2
+ module ActionControllerClientMixin
3
+ module ActionControllerMethods
4
+ ::CAS_USER_KEY = Cassiopeia::CONFIG[:current_user_key]
5
+ ::CAS_TICKET_ID_KEY = Cassiopeia::CONFIG[:ticket_id_key]
6
+ ::CAS_TICKET_KEY = Cassiopeia::CONFIG[:ticket_key]
7
+ def cas_current_ticket
8
+ session[CAS_TICKET_KEY] || params[CAS_TICKET_KEY]
9
+ end
10
+ def cas_current_ticket_id
11
+ params[CAS_TICKET_ID_KEY] || session[CAS_TICKET_ID_KEY]
12
+ end
13
+ def cas_store_current_user(ticket, user)
14
+ session[CAS_TICKET_KEY] = ticket
15
+ session[CAS_USER_KEY] = current_user
16
+ end
17
+ def cas_erase_current_user
18
+ cas_store_current_user nil, nil
19
+ session[CAS_TICKET_ID_KEY] = nil
20
+ end
21
+ def cas_current_ticket_valid?
22
+ logger.debug "\n Ticket.expires_at= #{cas_current_ticket[:expires_at]} \n" + "="*50 if cas_current_ticket
23
+ logger.debug "\nCurrent ticket valid: #{DateTime.parse(cas_current_ticket[:expires_at])} >= #{DateTime.now}\n" + "="*50 if cas_current_ticket && cas_current_ticket[:expires_at]
24
+ cas_current_ticket && DateTime.parse(cas_current_ticket[:expires_at]) >= DateTime.now if cas_current_ticket && cas_current_ticket[:expires_at]
25
+ end
26
+ def cas_request_ticket_id
27
+ store_location if respond_to?('store_location')
28
+ redirect_to Cassiopeia::Client::instance.cas_check_url(session)
29
+ end
30
+ def cas_request_current_user
31
+ session[CAS_TICKET_ID_KEY] = cas_current_ticket_id
32
+ @ticket = Cassiopeia::Client::instance.cas_current_ticket(session)
33
+ @current_user = Cassiopeia::User.new(@ticket[:user])
34
+ logger.debug "\nCurrent user identified (#{@current_user.login}), storing to session\n" + "="*50
35
+ cas_store_current_user(@ticket, @current_user)
36
+ logger.debug "\nShould redirect user to #{session[:return_to]}\n" + "="*50
37
+ redirect_back_or_default root_path if respond_to?('redirect_back_or_default')
38
+ end
39
+ def cas_required_roles
40
+ self.class.cas_required_roles if self.class.respond_to? :cas_required_roles
41
+ end
42
+ def cas_check_required_roles
43
+ if cas_required_roles
44
+ logger.debug "\nCas check required roles #{cas_required_roles}...\n" + "="*50
45
+ cas_required_roles.each do |r|
46
+ raise Cassiopeia::AccessDeniedException.new "You don't have required roles for this controller" unless current_user.has_role? r
47
+ end
48
+ end
49
+ end
50
+ def cas_require_user
51
+ if !cas_current_ticket_id
52
+ logger.debug "\nNo cas_ticket_id, requesting from cassiopeia...\n" + "="*50
53
+ return cas_request_ticket_id
54
+ end
55
+ if cas_current_ticket && !cas_current_ticket_valid?
56
+ logger.debug "\nCurrent ticket is invalid, erasing current user\n" + "="*50
57
+ cas_erase_current_user
58
+ return cas_request_ticket_id
59
+ end
60
+ if cas_current_ticket_id && !cas_current_ticket
61
+ logger.debug "\nCas ticket_id is in request, retrieving current_ticket and user...\n\n" + "="*50
62
+ return cas_request_current_user
63
+ elsif cas_current_ticket
64
+ logger.debug "\nCurrent user session is valid (current user = #{current_user.login})\n" + "="*50
65
+ end
66
+ end
67
+ def current_user
68
+ return @current_user if @current_user
69
+ @current_user = session[CAS_USER_KEY]
70
+ end
71
+ end
72
+ def cas_required_roles
73
+ @required_roles
74
+ end
75
+ def cas_require_roles(*roles)
76
+ @required_roles = [] unless defined? @required_roles
77
+ @required_roles |= roles
78
+ logger.debug "\nCAS add required role #{roles}, now roles_required: #{@required_roles}...\n" + "="*50
79
+ end
80
+ def use_cas_authorization
81
+ @current_user = nil
82
+ before_filter :cas_require_user, :cas_check_required_roles
83
+ include ActionControllerMethods
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,142 @@
1
+ module Cassiopeia
2
+ autoload :TicketsControllerConfig, 'cassiopeia/tickets_controller_config'
3
+
4
+ module ActionControllerServerMixin
5
+ module ActionControllerMethods
6
+ def ActionControllerMethods.rolesMethod=(m)
7
+ @@rolesMethod = m
8
+ end
9
+ def ActionControllerMethods.ticketClass=(c)
10
+ @@ticketClass = c
11
+ end
12
+ def cas_ticket_id
13
+ session[@ticket_id_key] || params[@ticket_id_key]
14
+ end
15
+ def cas_service_url
16
+ session[@service_url_key] || params[@service_url_key]
17
+ end
18
+ def cas_service_id
19
+ session[@service_id_key] || params[@service_id_key]
20
+ end
21
+
22
+ def cas_require_config
23
+ raise "ticketClass should be set to use this functionality" unless @@ticketClass
24
+ end
25
+
26
+ def cas_store_params
27
+ @ticket_id_key = Cassiopeia::CONFIG[:ticket_id_key]
28
+ @service_id_key = Cassiopeia::CONFIG[:service_id_key]
29
+ @service_url_key = Cassiopeia::CONFIG[:service_url_key]
30
+ session[@ticket_id_key] = cas_ticket_id
31
+ session[@service_id_key] = cas_service_id
32
+ session[@service_url_key] = cas_service_url
33
+ end
34
+
35
+ def cas_create_or_find_ticket
36
+ if current_user && !(cas_current_ticket_exists?)
37
+ @ticket = @@ticketClass.new(:user_id => current_user.id)
38
+ @ticket.user = current_user
39
+ @ticket.service = cas_service_id
40
+ if @ticket.save
41
+ session[@ticket_id_key] = @ticket.identity
42
+ session[@service_id_key] = @ticket.service
43
+ else
44
+ @ticket = nil
45
+ end
46
+ else
47
+ @ticket = @@ticketClass.find_by_identity(cas_ticket_id.to_s)
48
+ end
49
+ @ticket
50
+ end
51
+
52
+ def cas_current_ticket
53
+ @ticket = cas_create_or_find_ticket unless @ticket
54
+ logger.debug "\nCurrentTicket = #{@ticket.identity if @ticket}\n" + "="*50
55
+ @ticket
56
+ end
57
+
58
+ def cas_current_ticket_exists?
59
+ logger.debug "\nTicketExists = #{@@ticketClass.exists?(cas_ticket_id)}\n" + "="*50
60
+ @@ticketClass.exists?(cas_ticket_id)
61
+ end
62
+
63
+ def cas_current_ticket_valid?
64
+ logger.debug "\nTicketValid = #{cas_current_ticket.valid_for?(cas_service_id)}\n" + "="*50 if cas_current_ticket_exists?
65
+ cas_current_ticket.valid_for?(cas_service_id) if cas_current_ticket_exists?
66
+ end
67
+
68
+ def cas_respond_current_ticket
69
+ ticket_hash = cas_current_ticket.attributes
70
+ if cas_current_ticket.user
71
+ user_hash = cas_current_ticket.user.attributes
72
+ roles = []
73
+ roles = (cas_current_ticket.user.send @@rolesMethod) if cas_current_ticket.user.respond_to? @@rolesMethod
74
+ roles_hash = roles
75
+ user_hash[:roles] = roles_hash
76
+ else
77
+ user_hash = nil
78
+ end
79
+ ticket_hash = ticket_hash.merge({:user => user_hash}) if cas_current_ticket.user
80
+ logger.debug "\n Rendering ticket: \n"
81
+ logger.debug ticket_hash.to_json + "\n" + "="*50
82
+ simple_rest ticket_hash, {:status => :ok}
83
+ end
84
+
85
+ def cas_process_request
86
+ if current_user
87
+ cas_respond_current_ticket
88
+ elsif cas_current_ticket_exists?
89
+ cas_respond_current_ticket
90
+ else
91
+ @res = {:error => "Ticket not found"}
92
+ simple_rest @res, {:status => :error}
93
+ end
94
+ end
95
+
96
+ def cas_proceed_auth
97
+ if cas_current_ticket_valid? && current_user
98
+ logger.debug "\nCurrentTicketValid, current_user exists redirecting to service...\n" + "="*50
99
+ return redirect_to Cassiopeia::Server::instance.service_url(session)
100
+ elsif current_user
101
+ logger.debug "\nCurrentTicketInvalid, but current_user exists, should create new ticket...\n" + "="*50
102
+ cas_current_ticket.destroy
103
+ cas_create_or_find_ticket
104
+ return redirect_to Cassiopeia::Server::instance.service_url(session)
105
+ elsif cas_current_ticket_exists?
106
+ logger.debug "\nCurrentTicketInvalid, but current_user exists, destroying ticket, redirecting to login...\n" + "="*50
107
+ cas_current_ticket.destroy
108
+ end
109
+ redirect_to login_url
110
+ end
111
+
112
+ def create
113
+ cas_process_request
114
+ end
115
+
116
+ def show
117
+ cas_proceed_auth
118
+ end
119
+
120
+ def index
121
+ cas_proceed_auth
122
+ end
123
+
124
+ def detroy(params)
125
+ cas_current_ticket.destroy if cas_current_ticket_exists?
126
+ simple_rest nil, {:status => :ok}
127
+ end
128
+ end
129
+ def acts_as_cas_controller
130
+ defaultTicketClass = Ticket if defined? Ticket
131
+ controllerConfig = Cassiopeia::TicketsControllerConfig.new defaultTicketClass, :roles
132
+ yield controllerConfig
133
+ ActionControllerMethods.rolesMethod = controllerConfig.rolesMethod
134
+ ActionControllerMethods.ticketClass = controllerConfig.ticketClass
135
+ ActiveRecordServerMixin.ticketClass = controllerConfig.ticketClass
136
+ skip_before_filter :verify_authenticity_token, :only=> [:create, :index]
137
+ before_filter :require_user, :except => [:create, :index]
138
+ before_filter :cas_store_params, :cas_create_or_find_ticket, :cas_require_config
139
+ include ActionControllerMethods
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,36 @@
1
+ require 'uuidtools'
2
+
3
+ module Cassiopeia
4
+ module ActiveRecordServerMixin
5
+ def ActiveRecordServerMixin.ticketClass=(c)
6
+ @@ticketClass = c
7
+ end
8
+ # cas ticket
9
+ def acts_as_cas_ticket
10
+ class_eval do
11
+ def valid_for?(service)
12
+ return false unless identity && user
13
+ (ticket = self.for_service service) && ticket.expires_at >= DateTime.now
14
+ end
15
+ def for_service(service)
16
+ @@ticketClass.find(:first, :conditions => {:service => service, :user_id => user.id })
17
+ end
18
+ end
19
+ instance_eval do
20
+ def generate_expiration
21
+ DateTime.now() + Cassiopeia::CONFIG[:ticket_max_lifetime] / 24.0 / 60.0
22
+ end
23
+ def generate_uuid
24
+ UUIDTools::UUID.timestamp_create.to_s
25
+ end
26
+ def exists?(ticket_id)
27
+ self.find_by_identity(ticket_id.to_s) != nil
28
+ end
29
+ before_create do |obj|
30
+ obj.identity = generate_uuid
31
+ obj.expires_at = generate_expiration
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ module Cassiopeia
2
+ class Base
3
+ protected
4
+ def query_to_hash(query)
5
+ CGI.parse(query)
6
+ end
7
+
8
+ def hash_to_query(hash)
9
+ pairs = []
10
+ hash.each do |k, vals|
11
+ vals = [vals] unless vals.kind_of? Array
12
+ vals.each {|v| pairs << "#{CGI.escape(k.to_s)}=#{(v)?CGI.escape(v.to_s):''}"}
13
+ end
14
+ pairs.join("&")
15
+ end
16
+ end
17
+
18
+ class AccessDeniedException < Exception
19
+ end
20
+ end
@@ -0,0 +1,59 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'net/https'
4
+ require 'rexml/document'
5
+
6
+ ##################
7
+ # Client
8
+ ##################
9
+ module Cassiopeia
10
+ class Client < Base
11
+ private
12
+ @instance = nil
13
+ def server_url
14
+ Cassiopeia::CONFIG[:server_url] + "/" + Cassiopeia::CONFIG[:server_controller] + "." + Cassiopeia::CONFIG[:format]
15
+ end
16
+
17
+ def cas_data(session)
18
+ {
19
+ Cassiopeia::CONFIG[:service_url_key] => Cassiopeia::CONFIG[:service_url],
20
+ Cassiopeia::CONFIG[:service_id_key] => Cassiopeia::CONFIG[:service_id],
21
+ Cassiopeia::CONFIG[:ticket_id_key] => session[Cassiopeia::CONFIG[:ticket_id_key]]
22
+ }
23
+ end
24
+
25
+ # Submits some data to the given URI and returns a Net::HTTPResponse.
26
+ def do_post(uri, data)
27
+ uri = URI.parse(uri) unless uri.kind_of? URI
28
+ req = Net::HTTP::Post.new(uri.path)
29
+ req.set_form_data(data, ';')
30
+ https = Net::HTTP.new(uri.host, uri.port)
31
+ https.use_ssl = (uri.scheme == 'https')
32
+ res = https.start {|conn| conn.request(req) }
33
+ end
34
+
35
+ public
36
+ def self.instance
37
+ return @instance if @instance
38
+ @instance = Cassiopeia::Client.new
39
+ end
40
+
41
+ def cas_current_ticket(session)
42
+ res = do_post(server_url, cas_data(session))
43
+ case res
44
+ when Net::HTTPSuccess
45
+ begin
46
+ return ActiveSupport::JSON.decode(res.body).symbolize_keys if Cassiopeia::CONFIG[:format] == "js"
47
+ rescue
48
+ end
49
+ end
50
+ return {}
51
+ end
52
+
53
+ def cas_check_url(session)
54
+ server_url + "?" + hash_to_query(cas_data(session))
55
+ end
56
+
57
+ end
58
+ end
59
+
@@ -0,0 +1,27 @@
1
+ require 'yaml'
2
+
3
+ module Cassiopeia
4
+ private
5
+ DEFAULT_CONFIG = {
6
+ :ticket_max_lifetime => 120,
7
+ :server_controller => "cas",
8
+ :session_id_key => "cassiopeia_sesion_id",
9
+ :ticket_id_key => "ticket_id",
10
+ :service_id_key => "service_id",
11
+ :service_url_key => "service_url",
12
+ :server_url => "https://localhost/cassiopeia",
13
+ :service_url => "https://localhost/test_rails",
14
+ :service_id => "test",
15
+ :current_user_key => "current_user"
16
+ }
17
+ CONFIG_PATH = "#{RAILS_ROOT}/config/cassiopeia.yml"
18
+ @@conf = {}
19
+ if !File.exist?(CONFIG_PATH)
20
+ raise "Cassiopeia config required! Please, create RAILS_ROOT/conf/cassiopeia.yml file with server/service url"
21
+ end
22
+ @@conf = YAML::load(ERB.new((IO.read(CONFIG_PATH))).result).symbolize_keys if File.exist?(CONFIG_PATH)
23
+ @@ticketClass = Class.class
24
+ @@controllerClass = Class.class
25
+ public
26
+ CONFIG = DEFAULT_CONFIG.merge(@@conf)
27
+ end
@@ -0,0 +1,22 @@
1
+ module Cassiopeia
2
+ class Server < Base
3
+ private
4
+ @instance = nil
5
+ def cas_data(session)
6
+ {
7
+ Cassiopeia::CONFIG[:ticket_id_key] => session[Cassiopeia::CONFIG[:ticket_id_key]]
8
+ }
9
+ end
10
+ public
11
+ def self.instance
12
+ return @instance if @instance
13
+ @instance = Cassiopeia::Server.new
14
+ end
15
+
16
+ def service_url(session)
17
+ session[Cassiopeia::CONFIG[:service_url_key]] + "?" + hash_to_query(cas_data(session))
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,10 @@
1
+ module Cassiopeia
2
+ class TicketsControllerConfig
3
+ attr_accessor :ticketClass
4
+ attr_accessor :rolesMethod
5
+ def initialize(tClass, rMethod)
6
+ @ticketClass = tClass
7
+ @rolesMethod = rMethod
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'ostruct'
2
+
3
+ module Cassiopeia
4
+ class User < OpenStruct
5
+ def initialize(hash)
6
+ super(hash)
7
+ end
8
+
9
+ def has_role?(role)
10
+ roles && roles.respond_to?(:include?) && roles.include?(role.to_s)
11
+ end
12
+ end
13
+ end
data/lib/cassiopeia.rb ADDED
@@ -0,0 +1,26 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+ module Cassiopeia
4
+ VERSION = '0.0.1'
5
+ autoload :User, 'cassiopeia/user'
6
+ autoload :Base, 'cassiopeia/base'
7
+ autoload :AccessDeniedException, 'cassiopeia/base'
8
+ autoload :Server, 'cassiopeia/server'
9
+ autoload :Client, 'cassiopeia/client'
10
+ autoload :CONFIG, 'cassiopeia/config'
11
+ autoload :ActiveRecordServerMixin, 'cassiopeia/active_record_server_mixin'
12
+ autoload :ActionControllerServerMixin, 'cassiopeia/action_controller_server_mixin'
13
+ autoload :ActionControllerClientMixin, 'cassiopeia/action_controller_client_mixin'
14
+
15
+ class << self
16
+ def enable
17
+ ActionController::Base.send :extend, ActionControllerServerMixin
18
+ ActiveRecord::Base.send :extend, ActiveRecordServerMixin
19
+ ActionController::Base.send :extend, ActionControllerClientMixin
20
+ end
21
+ end
22
+ end
23
+
24
+ if defined? Rails && defined? RAILS_ROOT
25
+ Cassiopeia.enable
26
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cassiopeia
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - smecsia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-03-03 00:00:00 +03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: uuidtools
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rails
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.5
34
+ version:
35
+ description:
36
+ email: smecsia@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ files:
44
+ - README
45
+ - Rakefile
46
+ - lib/cassiopeia.rb
47
+ - lib/cassiopeia/active_record_server_mixin.rb
48
+ - lib/cassiopeia/action_controller_server_mixin.rb
49
+ - lib/cassiopeia/config.rb
50
+ - lib/cassiopeia/server.rb
51
+ - lib/cassiopeia/user.rb
52
+ - lib/cassiopeia/client.rb
53
+ - lib/cassiopeia/tickets_controller_config.rb
54
+ - lib/cassiopeia/action_controller_client_mixin.rb
55
+ - lib/cassiopeia/base.rb
56
+ has_rdoc: true
57
+ homepage:
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.5
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Rails plugin for custom CAS(Cassiopeia) server/client implementation
84
+ test_files: []
85
+