cassiopeia 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+