cassette 1.0.0
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.
- checksums.yaml +15 -0
- data/README.md +106 -0
- data/lib/cassette/authentication/authorities.rb +37 -0
- data/lib/cassette/authentication/cache.rb +30 -0
- data/lib/cassette/authentication/filter.rb +41 -0
- data/lib/cassette/authentication/user.rb +27 -0
- data/lib/cassette/authentication.rb +72 -0
- data/lib/cassette/cache.rb +42 -0
- data/lib/cassette/client/cache.rb +43 -0
- data/lib/cassette/client.rb +68 -0
- data/lib/cassette/errors/not_a_customer.rb +14 -0
- data/lib/cassette/errors/not_an_employee.rb +14 -0
- data/lib/cassette/errors.rb +44 -0
- data/lib/cassette/rubycas/helper.rb +78 -0
- data/lib/cassette/rubycas/not_single_sign_out_constraint.rb +14 -0
- data/lib/cassette/rubycas/single_sign_out_constraint.rb +27 -0
- data/lib/cassette/rubycas.rb +11 -0
- data/lib/cassette/version.rb +15 -0
- data/lib/cassette.rb +75 -0
- data/spec/cas/authentication/authorities_spec.rb +82 -0
- data/spec/cas/authentication/cache_spec.rb +8 -0
- data/spec/cas/authentication/filter_spec.rb +172 -0
- data/spec/cas/authentication/user_spec.rb +70 -0
- data/spec/cas/authentication_spec.rb +84 -0
- data/spec/cas/cache_spec.rb +40 -0
- data/spec/cas/client/cache_spec.rb +7 -0
- data/spec/cas/errors_spec.rb +29 -0
- data/spec/cas_spec.rb +78 -0
- data/spec/config.yml +5 -0
- data/spec/fixtures/cas/fail.xml +6 -0
- data/spec/fixtures/cas/success.xml +12 -0
- data/spec/integration/cas/client_spec.rb +50 -0
- data/spec/spec_helper.rb +27 -0
- metadata +257 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MzAxZTBjNTNjZjEwYWY0YzQ4Y2U0YjliYzFhMzYyNjAyM2Y1NDUxYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NDMwZGU1NmEzNWNjMDAxODY2NjNmODgwNDM5MDgwM2QzY2VlZTBjYg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OWNmZjk2MGUyMjI5NzkwNjhkMDczOTI2NjJkZWVjMDgxYzRlZjBkZDRjYWM0
|
10
|
+
ODUyZjAzMTBiOGMyM2ZhYWMyZTc4MDhlN2Q1ZmRlZWJhMzQ1MGYyYmVmZWZk
|
11
|
+
MWI5YzAxZDgzMTQ4NDQ3NjVkYjQxM2RhMDIwZmY4Zjg2MTI4ZWE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Yzc1ZjM0MTk4OTQyYTJmYTMxODRiYWRjOWY0N2ZjMDBmMDcwZDI4ZDYxOTg1
|
14
|
+
YTI5M2U3ODhhZTMyNjc4MGYyZDE5OTU2MzgxODQxOWY1NDIyMmI3MTU4Yjg1
|
15
|
+
MjIxOWRhNWFiMDc2YTIxYjVhZjNjNGY4NGNhNjkyOTgxNmFlZjY=
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Cassette::Client
|
2
|
+
|
3
|
+
Library to generate and validate STs and TGTs
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'cassette'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
Require this library and create an intializer to set its configuration:
|
18
|
+
|
19
|
+
|
20
|
+
Cassette.config = config
|
21
|
+
|
22
|
+
|
23
|
+
where config is an object that responds to the methods #base for the base CAS uri, #username and #password
|
24
|
+
if you are authenticating on other systems and #service and #base\_authority if you are using the authentication filter
|
25
|
+
to authenticate your app
|
26
|
+
|
27
|
+
|
28
|
+
You may also set the caching backend using the .backend= module method:
|
29
|
+
|
30
|
+
|
31
|
+
Cassette::Cache.backend = ActiveSupport::Cache::MemcacheStorage.new
|
32
|
+
|
33
|
+
|
34
|
+
By default, Cassette::Cache will check if you have Rails.cache defined or instantiate a new ActiveSupport::Cache::MemoryStore
|
35
|
+
|
36
|
+
|
37
|
+
To authenticate your Rails app, add to your ApplicationController (or any authenticated controller):
|
38
|
+
|
39
|
+
|
40
|
+
class ApplicationController < ActionController::Base
|
41
|
+
include Cassette::Authentication::Filter
|
42
|
+
|
43
|
+
|
44
|
+
(...)
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
You should also rescue from Cassette::Errors::Forbidden with more friendly errors
|
50
|
+
|
51
|
+
If you wish to have actions that skip the authentication filter, add to your controller:
|
52
|
+
|
53
|
+
|
54
|
+
skip_authentication [options]
|
55
|
+
|
56
|
+
|
57
|
+
Where options are the same options you can pass to Rails' __skip_before_filter__ method
|
58
|
+
|
59
|
+
## RubyCAS client helpers
|
60
|
+
|
61
|
+
|
62
|
+
If you are authenticating users with RubyCAS and want role checking, in your rubycas initializer:
|
63
|
+
|
64
|
+
|
65
|
+
require "cas/rubycas"
|
66
|
+
|
67
|
+
|
68
|
+
And in your ApplicationController (or any authenticated controller):
|
69
|
+
|
70
|
+
|
71
|
+
include Cassette::Rubycas::Helper
|
72
|
+
|
73
|
+
# - Allow only employees:
|
74
|
+
#
|
75
|
+
# before_filter :employee_only_filter
|
76
|
+
#
|
77
|
+
# rescue_from Cassette::Errors::NotAnEmployee d
|
78
|
+
# redirect_to '/403.html'
|
79
|
+
# end
|
80
|
+
|
81
|
+
# - Allow only customers:
|
82
|
+
#
|
83
|
+
# before_filter :customer_only_filter
|
84
|
+
#
|
85
|
+
# rescue_from Cassette::Errors::NotACustomer do
|
86
|
+
# redirect_to '/403.html'
|
87
|
+
# end
|
88
|
+
|
89
|
+
|
90
|
+
## Instantiating Cassette::Client and Cassette::Authentication
|
91
|
+
|
92
|
+
You can create your own instances of __Cassette::Client__ (st/tgt generator) and __Cassette::Authentication__ (st validator).
|
93
|
+
|
94
|
+
The constructor accepts a hash with keys (as symbols) for the values of cache, logger, http_client and configuration.
|
95
|
+
|
96
|
+
All values default to the same values used when accessing the class methods directly.
|
97
|
+
|
98
|
+
Please check the constructors or integration specs for details.
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
1. Fork it
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create new Pull Request
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "cassette/authentication"
|
4
|
+
|
5
|
+
class Cassette::Authentication::Authorities
|
6
|
+
def self.parse(authorities, base_authority = nil)
|
7
|
+
new(authorities, base_authority)
|
8
|
+
end
|
9
|
+
|
10
|
+
def base
|
11
|
+
@base_authority.to_s.upcase
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_raw_role?(role)
|
15
|
+
return true if ENV["NOAUTH"]
|
16
|
+
@authorities.include?(role)
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_role?(role)
|
20
|
+
return true if ENV["NOAUTH"]
|
21
|
+
has_raw_role?("#{base}_#{role.to_s.upcase.gsub("_", "-")}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(authorities, base_authority = nil)
|
25
|
+
@base_authority = base_authority || Cassette.config.base_authority
|
26
|
+
|
27
|
+
if authorities.is_a?(String)
|
28
|
+
@authorities = authorities.gsub(/^\[(.*)\]$/, "\\1").split(",").map(&:strip)
|
29
|
+
else
|
30
|
+
@authorities = Array(authorities).map(&:strip)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def authorities
|
35
|
+
@authorities.dup
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "cassette/authentication"
|
4
|
+
require "cassette/cache"
|
5
|
+
|
6
|
+
class Cassette::Authentication::Cache
|
7
|
+
include Cassette::Cache
|
8
|
+
|
9
|
+
def initialize(logger)
|
10
|
+
self.logger = logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch_authentication(ticket, options = {}, &block)
|
14
|
+
options = {expires_in: 5 * 60, max_uses: 5000, force: false}.merge(options)
|
15
|
+
fetch("Cassette::Authentication.validate_ticket(#{ticket})", options) do
|
16
|
+
logger.info("Authentication for #{ticket} is not cached")
|
17
|
+
block.call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_authentication_cache!
|
22
|
+
backend.delete_matched("Cassette::Authentication.validate_ticket*")
|
23
|
+
backend.delete_matched("#{uses_key("Cassette::Authentication.validate_ticket")}*")
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
attr_accessor :logger
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
require "cassette/authentication/user"
|
5
|
+
|
6
|
+
module Cassette::Authentication::Filter
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do |controller|
|
10
|
+
controller.before_filter(:validate_authentication_ticket)
|
11
|
+
controller.send(:attr_accessor, :current_user)
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def skip_authentication(*options)
|
16
|
+
skip_before_filter :validate_authentication_ticket, *options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_authentication_ticket(service = Cassette.config.service)
|
21
|
+
ticket = request.headers["Service-Ticket"] || params[:ticket]
|
22
|
+
|
23
|
+
if ENV["NOAUTH"] && !ticket
|
24
|
+
Cassette.logger.debug "NOAUTH set and no Service Ticket, skipping authentication"
|
25
|
+
self.current_user = Cassette::Authentication::User.new
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
self.current_user = Cassette::Authentication.validate_ticket(ticket, service)
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_role!(role)
|
33
|
+
return if ENV["NOAUTH"]
|
34
|
+
raise Cassette::Errors::Forbidden unless current_user.has_role?(role)
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_raw_role!(role)
|
38
|
+
return if ENV["NOAUTH"]
|
39
|
+
raise Cassette::Errors::Forbidden unless current_user.has_raw_role?(role)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "cassette/authentication"
|
4
|
+
require "cassette/authentication/authorities"
|
5
|
+
require "delegate"
|
6
|
+
|
7
|
+
class Cassette::Authentication::User
|
8
|
+
attr_accessor :login, :name, :authorities, :email, :ticket
|
9
|
+
delegate :has_role?, :has_raw_role?, to: :@authorities
|
10
|
+
|
11
|
+
def initialize(attrs = {})
|
12
|
+
config = attrs[:config]
|
13
|
+
@login = attrs[:login]
|
14
|
+
@name = attrs[:name]
|
15
|
+
@type = attrs[:type]
|
16
|
+
@email = attrs[:email]
|
17
|
+
@ticket = attrs[:ticket]
|
18
|
+
@authorities = Cassette::Authentication::Authorities
|
19
|
+
.parse(attrs.fetch(:authorities, "[]"), config && config.base_authority)
|
20
|
+
end
|
21
|
+
|
22
|
+
%w(customer employee).each do |type|
|
23
|
+
define_method :"#{type}?" do
|
24
|
+
!@type.nil? && @type.to_s.downcase == type.to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "active_support/xml_mini"
|
4
|
+
ActiveSupport::XmlMini.backend = 'LibXML'
|
5
|
+
|
6
|
+
module Cassette
|
7
|
+
class Authentication
|
8
|
+
def self.method_missing(name, *args)
|
9
|
+
@@default_authentication ||= new
|
10
|
+
@@default_authentication.send(name, *args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(opts = {})
|
14
|
+
self.config = opts.fetch(:config, Cassette.config)
|
15
|
+
self.logger = opts.fetch(:logger, Cassette.logger)
|
16
|
+
self.http = opts.fetch(:http_client, Cassette)
|
17
|
+
self.cache = opts.fetch(:cache, Cassette::Authentication::Cache.new(logger))
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_ticket(ticket, service = config.service)
|
21
|
+
logger.debug "Cassette::Authentication validating ticket: #{ticket}"
|
22
|
+
raise Cassette::Errors::AuthorizationRequired if ticket.nil? || ticket.blank?
|
23
|
+
|
24
|
+
user = ticket_user(ticket, service)
|
25
|
+
logger.info "Cassette::Authentication user: #{user.inspect}"
|
26
|
+
|
27
|
+
raise Cassette::Errors::Forbidden unless user
|
28
|
+
|
29
|
+
user
|
30
|
+
end
|
31
|
+
|
32
|
+
def ticket_user(ticket, service = config.service)
|
33
|
+
cache.fetch_authentication(ticket) do
|
34
|
+
begin
|
35
|
+
logger.info("Validating #{ticket} on #{validate_uri}")
|
36
|
+
response = http.post(validate_uri, ticket: ticket, service: service).body
|
37
|
+
|
38
|
+
logger.info("Validation resut: #{response.inspect}")
|
39
|
+
|
40
|
+
user = nil
|
41
|
+
|
42
|
+
ActiveSupport::XmlMini.with_backend("LibXML") do
|
43
|
+
result = ActiveSupport::XmlMini.parse(response)
|
44
|
+
|
45
|
+
login = result.try(:[], "serviceResponse").try(:[], "authenticationSuccess").try(:[], "user").try(:[], "__content__")
|
46
|
+
|
47
|
+
if login
|
48
|
+
attributes = result["serviceResponse"]["authenticationSuccess"]["attributes"]
|
49
|
+
name = attributes.try(:[], "cn").try(:[], "__content__")
|
50
|
+
authorities = attributes.try(:[], "authorities").try(:[], "__content__")
|
51
|
+
|
52
|
+
user = Cassette::Authentication::User.new(login: login, name: name, authorities: authorities, ticket: ticket, config: config)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
user
|
57
|
+
rescue => exception
|
58
|
+
logger.error "Error while authenticating ticket #{ticket}: #{exception.message}"
|
59
|
+
raise Cassette::Errors::Forbidden.new(exception.message)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
attr_accessor :cache, :logger, :http, :config
|
67
|
+
|
68
|
+
def validate_uri
|
69
|
+
"#{config.base.gsub(/\/?$/, "")}/serviceValidate"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "active_support/cache"
|
4
|
+
|
5
|
+
module Cassette
|
6
|
+
module Cache
|
7
|
+
def backend
|
8
|
+
@backend ||= begin
|
9
|
+
if defined?(Rails) && Rails.cache
|
10
|
+
Rails.cache
|
11
|
+
else
|
12
|
+
ActiveSupport::Cache::MemoryStore.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def backend=(backend)
|
18
|
+
@backend = backend
|
19
|
+
end
|
20
|
+
|
21
|
+
def uses_key(key)
|
22
|
+
"uses:#{key}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch(key, options = {}, &block)
|
26
|
+
if options[:max_uses].to_i != 0
|
27
|
+
uses_key = self.uses_key(key)
|
28
|
+
uses = backend.read(uses_key, raw: true)
|
29
|
+
backend.write(uses_key, 0, raw: true, expires_in: options[:expires_in]) if uses.nil?
|
30
|
+
|
31
|
+
if uses.to_i >= options[:max_uses].to_i
|
32
|
+
options[:force] = true
|
33
|
+
backend.write(uses_key, 0, raw: true, expires_in: options[:expires_in])
|
34
|
+
else
|
35
|
+
backend.increment(uses_key)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
backend.fetch(key, options, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "cassette/client"
|
4
|
+
require "cassette/cache"
|
5
|
+
|
6
|
+
class Cassette::Client::Cache
|
7
|
+
include Cassette::Cache
|
8
|
+
|
9
|
+
def initialize(logger)
|
10
|
+
self.logger = logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch_tgt(options = {}, &block)
|
14
|
+
options = {expires_in: 4 * 3600, max_uses: 5000, force: false}.merge(options)
|
15
|
+
fetch("Cassette::Client.tgt", options) do
|
16
|
+
self.clear_st_cache!
|
17
|
+
logger.info "TGT is not cached"
|
18
|
+
yield
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch_st(service, options = {}, &block)
|
23
|
+
options = {max_uses: 2000, expires_in: 252, force: false}.merge(options)
|
24
|
+
fetch("Cassette::Client.st(#{service})", options) do
|
25
|
+
logger.info "ST for #{service} is not cached"
|
26
|
+
yield
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_tgt_cache!
|
31
|
+
backend.delete("Cassette::Client.tgt")
|
32
|
+
backend.delete("#{uses_key("Cassette::Client.tgt")}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear_st_cache!
|
36
|
+
backend.delete_matched("Cassette::Client.st*")
|
37
|
+
backend.delete_matched("#{uses_key("Cassette::Client.st")}*")
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
attr_accessor :logger
|
43
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Cassette
|
4
|
+
class Client
|
5
|
+
def self.method_missing(name, *args)
|
6
|
+
@@default_client ||= new
|
7
|
+
@@default_client.send(name, *args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(opts = {})
|
11
|
+
self.config = opts.fetch(:config, Cassette.config)
|
12
|
+
self.logger = opts.fetch(:logger, Cassette.logger)
|
13
|
+
self.http = opts.fetch(:http_client, Cassette)
|
14
|
+
self.cache = opts.fetch(:cache, Cassette::Client::Cache.new(logger))
|
15
|
+
end
|
16
|
+
|
17
|
+
def health_check
|
18
|
+
st_for("monitoring")
|
19
|
+
end
|
20
|
+
|
21
|
+
def tgt(usr, pwd, force = false)
|
22
|
+
logger.info "Requesting TGT"
|
23
|
+
cache.fetch_tgt(force: force) do
|
24
|
+
response = http.post(tickets_uri, username: usr, password: pwd)
|
25
|
+
tgt = $1 if response.headers["Location"] =~ /tickets\/(.*)/
|
26
|
+
logger.info "TGT is #{tgt}"
|
27
|
+
tgt
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def st(tgt, service, force = false)
|
32
|
+
logger.info "Requesting ST for #{service}"
|
33
|
+
cache.fetch_st(service, force: force) do
|
34
|
+
response = http.post("#{tickets_uri}/#{tgt}", service: service)
|
35
|
+
response.body.tap do |st|
|
36
|
+
logger.info "ST is #{st}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def st_for(service_name)
|
42
|
+
st_with_retry(config.username, config.password, service_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
attr_accessor :cache, :logger, :http, :config
|
48
|
+
|
49
|
+
def st_with_retry(user, pass, service)
|
50
|
+
retrying = false
|
51
|
+
begin
|
52
|
+
st(tgt(user, pass, retrying), service)
|
53
|
+
rescue Cassette::Errors::NotFound => e
|
54
|
+
unless retrying
|
55
|
+
logger.info "Got 404 response, regenerating TGT"
|
56
|
+
retrying = true
|
57
|
+
retry
|
58
|
+
end
|
59
|
+
raise e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def tickets_uri
|
64
|
+
"#{config.base.gsub(/\/?$/, "")}/v1/tickets"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "active_support/inflector"
|
4
|
+
|
5
|
+
module Cassette
|
6
|
+
module Errors
|
7
|
+
TYPES = {
|
8
|
+
401 => :authorization_required,
|
9
|
+
400 => :bad_request,
|
10
|
+
403 => :forbidden,
|
11
|
+
500 => :internal_server_error,
|
12
|
+
404 => :not_found,
|
13
|
+
412 => :precondition_failed,
|
14
|
+
}
|
15
|
+
|
16
|
+
def self.raise_by_code(code)
|
17
|
+
name = TYPES[code.to_i]
|
18
|
+
|
19
|
+
if name
|
20
|
+
raise error_class(name)
|
21
|
+
else
|
22
|
+
raise error_class(:internal_server_error)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.error_class(name)
|
27
|
+
"Cassette::Errors::#{name.to_s.camelize}".constantize
|
28
|
+
end
|
29
|
+
|
30
|
+
class Base < StandardError
|
31
|
+
def code
|
32
|
+
self.class.const_get("CODE")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
TYPES.each do |status, name|
|
37
|
+
const_set(name.to_s.camelize, Class.new(Errors::Base))
|
38
|
+
error_class(name).const_set("CODE", status)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require "cassette/errors/not_an_employee"
|
44
|
+
require "cassette/errors/not_a_customer"
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module Cassette
|
6
|
+
module Rubycas
|
7
|
+
module Helper
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
before_filter :validate_authentication_ticket
|
12
|
+
helper_method :current_user
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def skip_authentication(*options)
|
17
|
+
skip_before_filter :validate_authentication_ticket, *options
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_authentication_ticket
|
22
|
+
return if ENV["NOAUTH"]
|
23
|
+
::CASClient::Frameworks::Rails::Filter.filter(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def employee_only_filter
|
27
|
+
return if ENV["NOAUTH"] or current_user.blank?
|
28
|
+
raise Cassette::Errors::NotAnEmployee unless current_user.employee?
|
29
|
+
end
|
30
|
+
|
31
|
+
def customer_only_filter
|
32
|
+
return if ENV["NOAUTH"] or current_user.blank?
|
33
|
+
raise Cassette::Errors::NotACustomer unless current_user.customer?
|
34
|
+
end
|
35
|
+
|
36
|
+
def cas_logout(to = root_url)
|
37
|
+
session.destroy
|
38
|
+
::CASClient::Frameworks::Rails::Filter.logout(self, to)
|
39
|
+
end
|
40
|
+
|
41
|
+
def fake_user
|
42
|
+
Cassette::Authentication::User.new({
|
43
|
+
login: "fake.user",
|
44
|
+
name: "Fake User",
|
45
|
+
email: "fake.user@locaweb.com.br",
|
46
|
+
authorities: [],
|
47
|
+
type: "customer"
|
48
|
+
})
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_role!(role)
|
52
|
+
return if ENV["NOAUTH"]
|
53
|
+
raise Cassette::Errors::Forbidden unless current_user.has_role?(role)
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_raw_role!(role)
|
57
|
+
return if ENV["NOAUTH"]
|
58
|
+
raise Cassette::Errors::Forbidden unless current_user.has_raw_role?(role)
|
59
|
+
end
|
60
|
+
|
61
|
+
def current_user
|
62
|
+
return fake_user if ENV["NOAUTH"]
|
63
|
+
return nil unless session[:cas_user]
|
64
|
+
|
65
|
+
@current_user ||= begin
|
66
|
+
attributes = session[:cas_extra_attributes]
|
67
|
+
Cassette::Authentication::User.new({
|
68
|
+
login: session[:cas_user],
|
69
|
+
name: attributes.try(:[], :cn),
|
70
|
+
email: attributes.try(:[], :email),
|
71
|
+
authorities: attributes.try(:[], :authorities),
|
72
|
+
type: attributes.try(:[], :type).try(:downcase)
|
73
|
+
})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Cassette
|
4
|
+
module Rubycas
|
5
|
+
class SingleSignOutConstraint
|
6
|
+
def matches?(request)
|
7
|
+
if (content_type = request.headers["CONTENT_TYPE"]) &&
|
8
|
+
content_type =~ /^multipart\//
|
9
|
+
return false
|
10
|
+
end
|
11
|
+
|
12
|
+
if request.post? &&
|
13
|
+
request.request_parameters['logoutRequest'] &&
|
14
|
+
[request.request_parameters['logoutRequest'],
|
15
|
+
URI.unescape(request.request_parameters['logoutRequest'])]
|
16
|
+
.find { |xml| xml =~ /^<samlp:LogoutRequest.*?<samlp:SessionIndex>(.*)<\/samlp:SessionIndex>/m }
|
17
|
+
|
18
|
+
Cassette.logger.debug "Intercepted a single sign out request on #{request}"
|
19
|
+
return true
|
20
|
+
end
|
21
|
+
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|