cassette 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ module Cassette
2
+ class Version
3
+ MAJOR = "1"
4
+ MINOR = "0"
5
+
6
+ def self.build_number
7
+ ENV["BUILD_NUMBER"] || 0
8
+ end
9
+
10
+ def self.version
11
+ [MAJOR, MINOR, build_number].join(".")
12
+ end
13
+ end
14
+ end
15
+
data/lib/cassette.rb ADDED
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+
3
+ require "cassette/errors"
4
+ require "cassette/cache"
5
+ require "cassette/client/cache"
6
+ require "cassette/client"
7
+ require "cassette/authentication"
8
+ require "cassette/authentication/authorities"
9
+ require "cassette/authentication/user"
10
+ require "cassette/authentication/cache"
11
+ require "cassette/authentication/filter"
12
+
13
+ require "faraday"
14
+ require "logger"
15
+
16
+ module Cassette
17
+ extend self
18
+
19
+ DEFAULT_TIMEOUT = 10
20
+
21
+ def logger
22
+ @@logger ||= begin
23
+ if defined?(Rails) && Rails.logger
24
+ Rails.logger
25
+ else
26
+ Logger.new("/dev/null")
27
+ end
28
+ end
29
+ end
30
+
31
+ def logger=(logger)
32
+ @@logger = logger
33
+ end
34
+
35
+ def config
36
+ @@config
37
+ end
38
+
39
+ def config=(config)
40
+ @@config = config
41
+ end
42
+
43
+ def new_request(uri, timeout)
44
+ Faraday.new(url: uri, ssl: { verify: false, version: "TLSv1" }) do |builder|
45
+ builder.adapter :httpclient
46
+ builder.options.timeout = timeout
47
+ end
48
+ end
49
+
50
+ def get(uri, payload, timeout = DEFAULT_TIMEOUT)
51
+ perform(:get, uri, payload, timeout) do |req|
52
+ req.params = payload
53
+ logger.debug "Request: #{req.inspect}"
54
+ end
55
+ end
56
+
57
+ def post(uri, payload, timeout = DEFAULT_TIMEOUT)
58
+ perform(:post, uri, payload, timeout) do |req|
59
+ req.body = payload
60
+ logger.debug "Request: #{req.inspect}"
61
+ end
62
+ end
63
+
64
+ protected
65
+
66
+ def perform(op, uri, payload, timeout = DEFAULT_TIMEOUT, &block)
67
+ request = new_request(uri, timeout)
68
+ res = request.send(op, &block)
69
+
70
+ res.tap do |response|
71
+ logger.debug "Got response: #{response.body.inspect} (#{response.status}), #{response.headers.inspect}"
72
+ Cassette::Errors.raise_by_code(response.status) unless response.success?
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cassette::Authentication::Authorities do
4
+ subject do
5
+ Cassette::Authentication::Authorities
6
+ end
7
+
8
+ describe "#has_role?" do
9
+ let(:input) { "[#{Cassette.config.base_authority}, SAPI, #{Cassette.config.base_authority}_CREATE-USER]" }
10
+ let(:authorities) { subject.parse(input) }
11
+
12
+ it "adds the application prefix to roles" do
13
+ expect(authorities.has_role?("CREATE-USER")).to eql(true)
14
+ end
15
+
16
+ it "ignores role case" do
17
+ expect(authorities.has_role?("create-user")).to eql(true)
18
+ end
19
+
20
+ it "replaces underscores with dashes" do
21
+ expect(authorities.has_role?("create_user")).to eql(true)
22
+ end
23
+ end
24
+
25
+ context "with a defined base authority" do
26
+ let(:base_authority) { "SOMEAPI" }
27
+
28
+ it "stores the base authority" do
29
+ input = "CUSTOMERAPI"
30
+ expect(subject.parse(input, base_authority).base).to eql(base_authority)
31
+ end
32
+
33
+ describe "#has_role?" do
34
+ let(:input) { "[#{Cassette.config.base_authority}_TEST2, SOMEAPI_TEST]" }
35
+
36
+ it "returns true for a role that is using the base authority" do
37
+ expect(subject.parse(input, base_authority)).to have_role(:test)
38
+ end
39
+
40
+ it "returns false for a role that is not using the base authority" do
41
+ expect(subject.parse(input, base_authority)).not_to have_role(:test2)
42
+ end
43
+ end
44
+ end
45
+
46
+ context "CAS authorities parsing" do
47
+ it "handles single authority" do
48
+ input = "CUSTOMERAPI"
49
+ expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI))
50
+ end
51
+
52
+ it "handles multiple authorities with surrounding []" do
53
+ input = "[CUSTOMERAPI, SAPI]"
54
+ expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI SAPI))
55
+ end
56
+
57
+ it "ignores whitespace in multiple authorities" do
58
+ input = "[CUSTOMERAPI,SAPI]"
59
+ expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI SAPI))
60
+ end
61
+
62
+ it "returns an empty array when input is nil" do
63
+ expect(subject.parse(nil).authorities).to eq([])
64
+ end
65
+ end
66
+
67
+ context "with authentication disabled" do
68
+ before { ENV["NOAUTH"] = "true" }
69
+ after { ENV.delete("NOAUTH") }
70
+ subject { Cassette::Authentication::Authorities.new("[]") }
71
+
72
+ it "#has_role? returns true for every role" do
73
+ expect(subject.authorities).to be_empty
74
+ expect(subject.has_role?(:can_manage)).to eql(true)
75
+ end
76
+
77
+ it "#has_raw_role? returns true for every role" do
78
+ expect(subject.authorities).to be_empty
79
+ expect(subject.has_raw_role?("SAPI_CUSTOMER-CREATOR")).to eql(true)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ describe Cassette::Authentication::Cache do
7
+ pending
8
+ end
@@ -0,0 +1,172 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+ require "active_support/core_ext/hash/indifferent_access"
5
+
6
+ describe Cassette::Authentication::Filter do
7
+ before do
8
+ allow(Cassette::Authentication).to receive(:validate_ticket)
9
+ end
10
+
11
+ class ControllerMock
12
+ attr_accessor :params, :request, :current_user
13
+ def self.before_filter(*); end
14
+ include Cassette::Authentication::Filter
15
+
16
+ def initialize(params = {}, headers = {})
17
+ self.params = params.with_indifferent_access
18
+ self.request = OpenStruct.new(headers: headers.with_indifferent_access)
19
+ end
20
+ end
21
+
22
+ shared_context "with NOAUTH" do
23
+ before do
24
+ ENV["NOAUTH"] = "yes"
25
+ end
26
+
27
+ after do
28
+ ENV.delete("NOAUTH")
29
+ end
30
+ end
31
+
32
+ describe "#validate_raw_role!" do
33
+ let(:controller) { ControllerMock.new }
34
+ let(:current_user) { instance_double(Cassette::Authentication::User) }
35
+
36
+ before do
37
+ allow(controller).to receive(:current_user).and_return(current_user)
38
+ end
39
+
40
+ it_behaves_like "with NOAUTH" do
41
+ it "never checks the role" do
42
+ expect(current_user).not_to receive(:has_raw_role?)
43
+ controller.validate_raw_role!(:something)
44
+ end
45
+
46
+ it "does not raise error" do
47
+ expect { controller.validate_raw_role!(:something) }.not_to raise_error
48
+ end
49
+ end
50
+
51
+ it "forwards to current_user" do
52
+ role = instance_double(String)
53
+
54
+ expect(current_user).to receive(:has_raw_role?).with(role).and_return(true)
55
+ controller.validate_raw_role!(role)
56
+ end
57
+
58
+ it "raises a Cassette::Errors::Forbidden when current_user does not have the role" do
59
+ role = instance_double(String)
60
+
61
+ expect(current_user).to receive(:has_raw_role?).with(role).and_return(false)
62
+ expect { controller.validate_raw_role!(role) }.to raise_error(Cassette::Errors::Forbidden)
63
+ end
64
+ end
65
+
66
+ describe "#validate_role!" do
67
+ let(:controller) { ControllerMock.new }
68
+ let(:current_user) { instance_double(Cassette::Authentication::User) }
69
+
70
+ before do
71
+ allow(controller).to receive(:current_user).and_return(current_user)
72
+ end
73
+
74
+ it_behaves_like "with NOAUTH" do
75
+ it "never checks the role" do
76
+ expect(current_user).not_to receive(:has_role?)
77
+ controller.validate_role!(:something)
78
+ end
79
+
80
+ it "does not raise error" do
81
+ expect { controller.validate_role!(:something) }.not_to raise_error
82
+ end
83
+ end
84
+
85
+ it "forwards to current_user" do
86
+ role = instance_double(String)
87
+
88
+ expect(current_user).to receive(:has_role?).with(role).and_return(true)
89
+ controller.validate_role!(role)
90
+ end
91
+
92
+ it "raises a Cassette::Errors::Forbidden when current_user does not have the role" do
93
+ role = instance_double(String)
94
+
95
+ expect(current_user).to receive(:has_role?).with(role).and_return(false)
96
+ expect { controller.validate_role!(role) }.to raise_error(Cassette::Errors::Forbidden)
97
+ end
98
+ end
99
+
100
+ describe "#validate_authentication_ticket" do
101
+ it_behaves_like "with NOAUTH" do
102
+ context "and no ticket" do
103
+ let(:controller) { ControllerMock.new }
104
+
105
+ it "should not validate tickets" do
106
+ controller.validate_authentication_ticket
107
+ expect(Cassette::Authentication).not_to have_received(:validate_ticket)
108
+ end
109
+
110
+ it "should set current_user" do
111
+ controller.validate_authentication_ticket
112
+ expect(controller.current_user).to be_present
113
+ end
114
+ end
115
+
116
+ context "and a ticket header" do
117
+ let(:controller) do
118
+ ControllerMock.new({}, "Service-Ticket" => "le ticket")
119
+ end
120
+
121
+ it "should validate tickets" do
122
+ controller.validate_authentication_ticket
123
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
124
+ end
125
+ end
126
+
127
+ context "and a ticket param" do
128
+ let(:controller) do
129
+ ControllerMock.new(ticket: "le ticket")
130
+ end
131
+
132
+ it "should validate tickets" do
133
+ controller.validate_authentication_ticket
134
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
135
+ end
136
+ end
137
+ end
138
+
139
+ context "with a ticket in the query string *AND* headers" do
140
+ let(:controller) do
141
+ ControllerMock.new({"ticket" => "le other ticket"}, "Service-Ticket" => "le ticket")
142
+ end
143
+
144
+ it "should send only the header ticket to validation" do
145
+ controller.validate_authentication_ticket
146
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
147
+ end
148
+ end
149
+
150
+ context "with a ticket in the query string" do
151
+ let(:controller) do
152
+ ControllerMock.new("ticket" => "le ticket")
153
+ end
154
+
155
+ it "should send the ticket to validation" do
156
+ controller.validate_authentication_ticket
157
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
158
+ end
159
+ end
160
+
161
+ context "with a ticket in the Service-Ticket header" do
162
+ let(:controller) do
163
+ ControllerMock.new({}, "Service-Ticket" => "le ticket")
164
+ end
165
+
166
+ it "should send the ticket to validation" do
167
+ controller.validate_authentication_ticket
168
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cassette::Authentication::User do
4
+ let(:base_authority) do
5
+ Cassette.config.base_authority
6
+ end
7
+
8
+ describe "#initialize" do
9
+ context "without a config" do
10
+ it "forwards authorities parsing" do
11
+ expect(Cassette::Authentication::Authorities).to receive(:new).with("[CUSTOMERAPI, SAPI]", nil)
12
+ Cassette::Authentication::User.new(login: "john.doe", name: "John Doe", authorities: "[CUSTOMERAPI, SAPI]")
13
+ end
14
+ end
15
+
16
+ context "with a config" do
17
+ it "forwards authorities parsing passing along the base authority" do
18
+ config = object_double(Cassette.config)
19
+
20
+ expect(config).to receive(:base_authority).and_return("TESTAPI")
21
+ expect(Cassette::Authentication::Authorities).to receive(:new).with("[CUSTOMERAPI, SAPI]", "TESTAPI")
22
+
23
+ Cassette::Authentication::User.new(login: "john.doe", name: "John Doe", authorities: "[CUSTOMERAPI, SAPI]", config: config)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe "#has_role?" do
29
+ let (:user) do
30
+ Cassette::Authentication::User.new(login: "john.doe", name: "John Doe",
31
+ authorities: "[#{base_authority}, SAPI, #{base_authority}_CREATE-USER]")
32
+ end
33
+
34
+ it "adds the application prefix to roles" do
35
+ expect(user.has_role?("CREATE-USER")).to eql(true)
36
+ end
37
+
38
+ it "ignores role case" do
39
+ expect(user.has_role?("create-user")).to eql(true)
40
+ end
41
+
42
+ it "replaces underscores with dashes" do
43
+ expect(user.has_role?("create_user")).to eql(true)
44
+ end
45
+ end
46
+
47
+ context "user types" do
48
+ context "#employee?" do
49
+ it "returns true when user is an employee" do
50
+ expect(Cassette::Authentication::User.new(type: "employee")).to be_employee
51
+ expect(Cassette::Authentication::User.new(type: "Employee")).to be_employee
52
+ expect(Cassette::Authentication::User.new(type: :employee)).to be_employee
53
+ expect(Cassette::Authentication::User.new(type: "customer")).not_to be_employee
54
+ expect(Cassette::Authentication::User.new(type: nil)).not_to be_employee
55
+ expect(Cassette::Authentication::User.new(type: "")).not_to be_employee
56
+ end
57
+ end
58
+
59
+ context "#customer?" do
60
+ it "returns true when the user is a customer" do
61
+ expect(Cassette::Authentication::User.new(type: "customer")).to be_customer
62
+ expect(Cassette::Authentication::User.new(type: "Customer")).to be_customer
63
+ expect(Cassette::Authentication::User.new(type: :customer)).to be_customer
64
+ expect(Cassette::Authentication::User.new(type: "employee")).not_to be_customer
65
+ expect(Cassette::Authentication::User.new(type: nil)).not_to be_customer
66
+ expect(Cassette::Authentication::User.new(type: "")).not_to be_customer
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Cassette::Authentication do
6
+ let(:cache) { instance_double(Cassette::Authentication::Cache) }
7
+ let(:http) { class_double(Cassette) }
8
+
9
+ subject do
10
+ Cassette::Authentication.new(cache: cache, http_client: http)
11
+ end
12
+
13
+ describe "#ticket_user" do
14
+ context "when cached" do
15
+ it "returns the cached value when cached" do
16
+ cached = double('cached')
17
+
18
+ expect(cache).to receive(:fetch_authentication) do |ticket, &block|
19
+ expect(ticket).to eql("ticket")
20
+ expect(block).to be_present
21
+ cached
22
+ end
23
+
24
+ expect(subject.ticket_user("ticket")).to eql(cached)
25
+ end
26
+ end
27
+
28
+ context "when not cached" do
29
+ before do
30
+ expect(cache).to receive(:fetch_authentication) do |ticket, &block|
31
+ block.call
32
+ end
33
+ end
34
+
35
+ it "raises a Forbidden exception on any exceptions" do
36
+ allow(http).to receive(:post).with(anything, anything).and_raise(Cassette::Errors::BadRequest)
37
+ expect { subject.ticket_user("ticket") }.to raise_error(Cassette::Errors::Forbidden)
38
+ end
39
+
40
+ context "with a failed CAS response" do
41
+ before do
42
+ allow(http).to receive(:post).with(anything, anything)
43
+ .and_return(OpenStruct.new(body: fixture("cas/fail.xml")))
44
+ end
45
+
46
+ it "returns nil" do
47
+ expect(subject.ticket_user("ticket")).to be_nil
48
+ end
49
+ end
50
+
51
+ context "with a successful CAS response" do
52
+ before do
53
+ allow(http).to receive(:post).with(anything, anything)
54
+ .and_return(OpenStruct.new(body: fixture("cas/success.xml")))
55
+ end
56
+
57
+ it "returns an User" do
58
+ expect(subject.ticket_user("ticket")).to be_instance_of(Cassette::Authentication::User)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#validate_ticket" do
65
+ it "raises a authorization required error when no ticket is provided" do
66
+ expect { subject.validate_ticket(nil) }.to raise_error(Cassette::Errors::AuthorizationRequired)
67
+ end
68
+
69
+ it "raises a authorization required error when ticket is blank" do
70
+ expect { subject.validate_ticket("") }.to raise_error(Cassette::Errors::AuthorizationRequired)
71
+ end
72
+
73
+ it "raises a forbidden error when the associated user is not found" do
74
+ expect(subject).to receive(:ticket_user).with("ticket", Cassette.config.service).and_return(nil)
75
+ expect { subject.validate_ticket("ticket") }.to raise_error(Cassette::Errors::Forbidden)
76
+ end
77
+
78
+ it "returns the associated user" do
79
+ user = double('User')
80
+ expect(subject).to receive(:ticket_user).with("ticket", Cassette.config.service).and_return(user)
81
+ expect(subject.validate_ticket("ticket")).to eql(user)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Cassette::Cache do
6
+ subject do
7
+ c = Class.new
8
+ c.send(:include, Cassette::Cache)
9
+ c.new
10
+ end
11
+
12
+ describe "backend" do
13
+ before { subject.backend = nil }
14
+ after { subject.backend = nil }
15
+
16
+ it "sets the backend" do
17
+ backend = double('Backend')
18
+ subject.backend = backend
19
+ expect(subject.backend).to eql(backend)
20
+ end
21
+
22
+ it "defaults to rails backend" do
23
+ rails = double("Rails")
24
+ allow(rails).to receive(:cache).and_return(rails)
25
+ stub_const("Rails", rails)
26
+
27
+ expect(subject.backend).to eql(rails)
28
+ end
29
+ end
30
+
31
+ it "invalidates the cache after the configured number of uses" do
32
+ generator = double('Generator')
33
+ expect(generator).to receive(:generate).twice
34
+
35
+ 6.times do
36
+ subject.fetch("Generator", max_uses: 5) { generator.generate }
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Cassette::Client::Cache do
6
+ pending
7
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Cassette::Errors do
6
+ describe Cassette::Errors::Base do
7
+ describe "#code" do
8
+ it "returns the HTTP status code accordlingly" do
9
+ expect(Cassette::Errors::Forbidden.new.code).to eql(403)
10
+ expect(Cassette::Errors::NotFound.new.code).to eql(404)
11
+ expect(Cassette::Errors::InternalServerError.new.code).to eql(500)
12
+ end
13
+ end
14
+ end
15
+
16
+ describe ".raise_by_code" do
17
+ it "raises the correct exception for the status code" do
18
+ expect { Cassette::Errors.raise_by_code(404) }.to raise_error(Cassette::Errors::NotFound)
19
+ expect { Cassette::Errors.raise_by_code(403) }.to raise_error(Cassette::Errors::Forbidden)
20
+ expect { Cassette::Errors.raise_by_code(412) }.to raise_error(Cassette::Errors::PreconditionFailed)
21
+ expect { Cassette::Errors.raise_by_code(500) }.to raise_error(Cassette::Errors::InternalServerError)
22
+ end
23
+
24
+ it "raises internal server error for unmapped errors" do
25
+ expect { Cassette::Errors.raise_by_code(406) }.to raise_error(Cassette::Errors::InternalServerError)
26
+ expect { Cassette::Errors.raise_by_code(200) }.to raise_error(Cassette::Errors::InternalServerError)
27
+ end
28
+ end
29
+ end
data/spec/cas_spec.rb ADDED
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+
3
+ describe Cassette do
4
+ let(:uri) { "http://example.org/" }
5
+ let(:response) do
6
+ Faraday.new do |builder|
7
+ builder.adapter :test do |stub|
8
+ stub.post(uri, 'data') do |env|
9
+ headers = env.request_headers
10
+ [200, {}, "{ok: true}"]
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ let(:failed_response) do
17
+ Faraday.new do |builder|
18
+ builder.adapter :test do |stub|
19
+ stub.post(uri, 'data') do |env|
20
+ headers = env.request_headers
21
+ [500, {}, "{ok: false}"]
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ describe ".new_request" do
28
+ it "returns an instance" do
29
+ # damn coverage
30
+ expect(Cassette.new_request(uri, 5)).to be_instance_of(Faraday::Connection)
31
+ end
32
+ end
33
+
34
+ describe ".post" do
35
+ it "forwards requests" do
36
+ allow(Cassette).to receive(:new_request).with(uri, 5).and_return(response)
37
+ Cassette.post(uri, "data", 5)
38
+ end
39
+
40
+ it "raises an exception when failed" do
41
+ allow(Cassette).to receive(:new_request).with(uri, 5).and_return(failed_response)
42
+ expect { Cassette.post(uri, "data", 5) }.to raise_error(Cassette::Errors::InternalServerError)
43
+ end
44
+ end
45
+
46
+ def keeping_logger(&block)
47
+ original_logger = Cassette.logger
48
+ block.call
49
+ Cassette.logger = original_logger
50
+ end
51
+
52
+ describe ".logger" do
53
+ it "returns a default instance" do
54
+ expect(Cassette.logger).not_to be_nil
55
+ expect(Cassette.logger.kind_of?(Logger)).to eql(true)
56
+ end
57
+
58
+ it "returns rails logger when Rails is available" do
59
+ keeping_logger do
60
+ Cassette.logger = nil
61
+ rails = double("Rails")
62
+ expect(rails).to receive(:logger).and_return(rails).at_least(:once)
63
+ stub_const("Rails", rails)
64
+ expect(Cassette.logger).to eql(rails)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe ".logger=" do
70
+ let(:logger) { Logger.new(STDOUT) }
71
+ it "defines the logger instance" do
72
+ keeping_logger do
73
+ Cassette.logger = logger
74
+ expect(Cassette.logger).to eq(logger)
75
+ end
76
+ end
77
+ end
78
+ end
data/spec/config.yml ADDED
@@ -0,0 +1,5 @@
1
+ username: "test"
2
+ password: "inicial1234"
3
+ service: "test-api.devintegration.locaweb.com.br"
4
+ base: "https://systems-login.systemintegration.locaweb.com.br"
5
+ base_authority: "CASTEST"
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0"?>
2
+ <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
3
+ <cas:authenticationFailure code='INVALID_TICKET'>
4
+ ticket &#039;whatever&#039; não reconhecido
5
+ </cas:authenticationFailure>
6
+ </cas:serviceResponse>