cassette 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|