cassette 1.0.18 → 1.1.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -12
  3. data/lib/cassette/authentication/authorities.rb +34 -30
  4. data/lib/cassette/authentication/cache.rb +22 -18
  5. data/lib/cassette/authentication/filter.rb +52 -33
  6. data/lib/cassette/authentication/user.rb +20 -16
  7. data/lib/cassette/authentication.rb +39 -27
  8. data/lib/cassette/cache.rb +2 -2
  9. data/lib/cassette/client/cache.rb +39 -35
  10. data/lib/cassette/client.rb +5 -4
  11. data/lib/cassette/http/parsed_response.rb +20 -0
  12. data/lib/cassette/http/request.rb +44 -0
  13. data/lib/cassette/http/ticket_response.rb +48 -0
  14. data/lib/cassette/http.rb +8 -0
  15. data/lib/cassette/rubycas/helper.rb +2 -8
  16. data/lib/cassette/rubycas/routing_constraint.rb +23 -0
  17. data/lib/cassette/rubycas/single_sign_out_constraint.rb +8 -8
  18. data/lib/cassette/rubycas/user_factory.rb +14 -0
  19. data/lib/cassette/rubycas.rb +2 -0
  20. data/lib/cassette/version.rb +2 -2
  21. data/lib/cassette.rb +12 -50
  22. data/spec/cassette/authentication/authorities_spec.rb +1 -1
  23. data/spec/cassette/authentication/cache_spec.rb +40 -4
  24. data/spec/cassette/authentication/filter_spec.rb +106 -36
  25. data/spec/cassette/authentication/user_factory_spec.rb +42 -0
  26. data/spec/cassette/authentication/user_spec.rb +4 -3
  27. data/spec/cassette/authentication_spec.rb +24 -12
  28. data/spec/cassette/cache_spec.rb +0 -2
  29. data/spec/cassette/client/cache_spec.rb +1 -1
  30. data/spec/cassette/client_spec.rb +319 -0
  31. data/spec/cassette/errors_spec.rb +1 -1
  32. data/spec/cassette/http/parsed_response_spec.rb +27 -0
  33. data/spec/cassette/http/request_spec.rb +41 -0
  34. data/spec/cassette/http/ticket_response_spec.rb +41 -0
  35. data/spec/cassette/rubycas/routing_constraint_spec.rb +84 -0
  36. data/spec/cassette_spec.rb +36 -0
  37. data/spec/integration/cas/client_spec.rb +0 -3
  38. data/spec/spec_helper.rb +5 -0
  39. data/spec/support/controllers/controller_mock.rb +19 -0
  40. metadata +98 -36
  41. data/spec/cas_spec.rb +0 -78
@@ -0,0 +1,44 @@
1
+ module Cassette
2
+ module Http
3
+ module Request
4
+ extend self
5
+
6
+ def post(uri, payload, timeout = DEFAULT_TIMEOUT)
7
+ perform(:post, uri, timeout) do |request|
8
+ request.body = URI.encode_www_form(payload)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def perform(http_verb, uri, timeout, &block)
15
+ request(uri, timeout)
16
+ .tap(&log_request)
17
+ .public_send(http_verb, &block)
18
+ .tap(&check_response)
19
+ end
20
+
21
+ def request(uri, timeout)
22
+ Faraday.new(url: uri, ssl: { verify: false, version: 'TLSv1' }) do |con|
23
+ con.adapter Faraday.default_adapter
24
+ con.options.timeout = timeout
25
+ end
26
+ end
27
+
28
+ def log_request
29
+ lambda { |request| Cassette.logger.debug "Request: #{request.inspect}" }
30
+ end
31
+
32
+ def check_response
33
+ lambda do |resp|
34
+ Cassette.logger.debug(
35
+ "Got response: #{resp.body.inspect} (#{resp.status}), " \
36
+ "#{resp.headers.inspect}"
37
+ )
38
+
39
+ Cassette::Errors.raise_by_code(resp.status) unless resp.success?
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,48 @@
1
+ module Cassette
2
+ module Http
3
+ class TicketResponse
4
+ def initialize(response)
5
+ @content = ParsedResponse.new(response)
6
+ end
7
+
8
+ def login
9
+ fetch_val(
10
+ content,
11
+ 'serviceResponse',
12
+ 'authenticationSuccess',
13
+ 'user',
14
+ '__content__'
15
+ )
16
+ end
17
+
18
+ def name
19
+ fetch_val(attributes, 'cn', '__content__')
20
+ end
21
+
22
+ def authorities
23
+ fetch_val(attributes, 'authorities', '__content__')
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :content
29
+
30
+ def fetch_val(hash, *keys)
31
+ keys.reduce(hash, &access_key)
32
+ end
33
+
34
+ def access_key
35
+ lambda { |hash, key| hash.try(:[], key) }
36
+ end
37
+
38
+ def attributes
39
+ fetch_val(
40
+ content,
41
+ 'serviceResponse',
42
+ 'authenticationSuccess',
43
+ 'attributes'
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'http/request'
2
+ require_relative 'http/parsed_response'
3
+ require_relative 'http/ticket_response'
4
+
5
+ module Cassette
6
+ module Http
7
+ end
8
+ end
@@ -6,6 +6,7 @@ module Cassette
6
6
  module Rubycas
7
7
  module Helper
8
8
  extend ActiveSupport::Concern
9
+ extend UserFactory
9
10
 
10
11
  included do
11
12
  before_filter :validate_authentication_ticket
@@ -60,14 +61,7 @@ module Cassette
60
61
  return fake_user if ENV['NOAUTH']
61
62
  return nil unless session[:cas_user]
62
63
 
63
- @current_user ||= begin
64
- attributes = session[:cas_extra_attributes]
65
- Cassette::Authentication::User.new(login: session[:cas_user],
66
- name: attributes.try(:[], :cn),
67
- email: attributes.try(:[], :email),
68
- authorities: attributes.try(:[], :authorities),
69
- type: attributes.try(:[], :type).try(:downcase))
70
- end
64
+ @current_user ||= from_session(session)
71
65
  end
72
66
  end
73
67
  end
@@ -0,0 +1,23 @@
1
+ module Cassette
2
+ module Rubycas
3
+ class RoutingConstraint
4
+ include UserFactory
5
+
6
+ attr_reader :role, :options
7
+
8
+ def initialize(role, opts = {})
9
+ defaults = { raw: false }
10
+ @role = role
11
+ @options = defaults.merge(opts)
12
+ end
13
+
14
+ def matches?(request)
15
+ user = from_session(request.session)
16
+
17
+ meth = options[:raw] ? :has_raw_role? : :has_role?
18
+
19
+ user.send(meth, role)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -3,18 +3,18 @@
3
3
  module Cassette
4
4
  module Rubycas
5
5
  class SingleSignOutConstraint
6
+ LOGOUT_PAYLOAD_EXPR = %r{<samlp:LogoutRequest.*?<samlp:SessionIndex>(.*)<\/samlp:SessionIndex}m
7
+
8
+ def logout_request?(params)
9
+ [params['logoutRequest'], URI.unescape(params['logoutRequest'])].find { |xml| xml =~ LOGOUT_PAYLOAD_EXPR }
10
+ end
11
+
6
12
  def matches?(request)
7
- if (content_type = request.headers['CONTENT_TYPE']) &&
8
- content_type =~ /^multipart\//
13
+ if (content_type = request.headers['CONTENT_TYPE']) && content_type =~ %r{^multipart/}
9
14
  return false
10
15
  end
11
16
 
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
-
17
+ if request.post? && request.request_parameters['logoutRequest'] && logout_request?(request.request_parameters)
18
18
  Cassette.logger.debug "Intercepted a single sign out request on #{request}"
19
19
  return true
20
20
  end
@@ -0,0 +1,14 @@
1
+ module Cassette
2
+ module Rubycas
3
+ module UserFactory
4
+ def from_session(session)
5
+ attributes = session[:cas_extra_attributes]
6
+ Cassette::Authentication::User.new(login: session[:cas_user],
7
+ name: attributes.try(:[], :cn),
8
+ email: attributes.try(:[], :email),
9
+ authorities: attributes.try(:[], :authorities),
10
+ type: attributes.try(:[], :type).try(:downcase))
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,8 +1,10 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ require 'cassette/rubycas/user_factory'
3
4
  require 'cassette/rubycas/helper'
4
5
  require 'cassette/rubycas/single_sign_out_constraint'
5
6
  require 'cassette/rubycas/not_single_sign_out_constraint'
7
+ require 'cassette/rubycas/routing_constraint'
6
8
 
7
9
  module Cassette
8
10
  module Rubycas
@@ -1,8 +1,8 @@
1
1
  module Cassette
2
2
  class Version
3
3
  MAJOR = '1'
4
- MINOR = '0'
5
- PATCH = '18'
4
+ MINOR = '1'
5
+ PATCH = '0'
6
6
 
7
7
  def self.version
8
8
  [MAJOR, MINOR, PATCH].join('.')
data/lib/cassette.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'cassette/errors'
4
4
  require 'cassette/cache'
5
+ require 'cassette/http'
5
6
  require 'cassette/client/cache'
6
7
  require 'cassette/client'
7
8
  require 'cassette/authentication'
@@ -16,62 +17,23 @@ require 'logger'
16
17
  module Cassette
17
18
  extend self
18
19
 
20
+ attr_writer :config, :logger
21
+
19
22
  DEFAULT_TIMEOUT = 10
20
23
 
21
24
  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
25
+ @logger ||= begin
26
+ if defined?(::Rails) && ::Rails.logger
27
+ ::Rails.logger
28
+ else
29
+ Logger.new('/dev/null')
30
+ end
31
+ end
33
32
  end
34
33
 
35
34
  def config
36
- if defined?(@@config)
37
- @@config
38
- end
39
- end
40
-
41
- def config=(config)
42
- @@config = config
43
- end
44
-
45
- def new_request(uri, timeout)
46
- Faraday.new(url: uri, ssl: { verify: false, version: 'TLSv1' }) do |builder|
47
- builder.adapter Faraday.default_adapter
48
- builder.options.timeout = timeout
49
- end
35
+ @config if defined?(@config)
50
36
  end
51
37
 
52
- def get(uri, payload, timeout = DEFAULT_TIMEOUT)
53
- perform(:get, uri, payload, timeout) do |req|
54
- req.params = payload
55
- logger.debug "Request: #{req.inspect}"
56
- end
57
- end
58
-
59
- def post(uri, payload, timeout = DEFAULT_TIMEOUT)
60
- perform(:post, uri, payload, timeout) do |req|
61
- req.body = URI.encode_www_form(payload)
62
- logger.debug "Request: #{req.inspect}"
63
- end
64
- end
65
-
66
- protected
67
-
68
- def perform(op, uri, _payload, timeout = DEFAULT_TIMEOUT, &block)
69
- request = new_request(uri, timeout)
70
- res = request.send(op, &block)
71
-
72
- res.tap do |response|
73
- logger.debug "Got response: #{response.body.inspect} (#{response.status}), #{response.headers.inspect}"
74
- Cassette::Errors.raise_by_code(response.status) unless response.success?
75
- end
76
- end
38
+ delegate :post, to: :'Http::Request'
77
39
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+
2
2
 
3
3
  describe Cassette::Authentication::Authorities do
4
4
  subject do
@@ -1,8 +1,44 @@
1
-
2
1
  # encoding: utf-8
3
2
 
4
- require 'spec_helper'
5
-
6
3
  describe Cassette::Authentication::Cache do
7
- pending
4
+ subject(:cache) { described_class.new(Logger.new('/dev/null')) }
5
+
6
+ describe '#fetch_authentication' do
7
+ subject(:fetch_authentication) do
8
+ cache.fetch_authentication(ticket, service, &block)
9
+ end
10
+
11
+ let(:second_call) do
12
+ cache.fetch_authentication(ticket, service, &other_block)
13
+ end
14
+ let(:call_with_other_service) do
15
+ cache.fetch_authentication(ticket, other_service, &other_block)
16
+ end
17
+
18
+ let(:ticket) { 'ticket' }
19
+
20
+ let(:service) { 'lala' }
21
+ let(:other_service) { 'popo' }
22
+
23
+ let(:block) { -> { 1 } }
24
+ let(:other_block) { -> { 2 } }
25
+
26
+
27
+ before { cache.fetch_authentication(ticket, service, &block) }
28
+
29
+ it { is_expected.to eq(1) }
30
+
31
+ context 'when for a second time' do
32
+ it { expect(second_call).to eq(1) }
33
+
34
+ it do
35
+ expect(other_block).not_to receive(:call)
36
+ second_call
37
+ end
38
+
39
+ context 'when calling with a different service' do
40
+ it { expect(call_with_other_service).to eq(2) }
41
+ end
42
+ end
43
+ end
8
44
  end
@@ -1,24 +1,12 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'spec_helper'
4
- require 'active_support/core_ext/hash/indifferent_access'
3
+
5
4
 
6
5
  describe Cassette::Authentication::Filter do
7
6
  before do
8
7
  allow(Cassette::Authentication).to receive(:validate_ticket)
9
8
  end
10
9
 
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
10
  shared_context 'with NOAUTH' do
23
11
  before do
24
12
  ENV['NOAUTH'] = 'yes'
@@ -30,7 +18,7 @@ describe Cassette::Authentication::Filter do
30
18
  end
31
19
 
32
20
  describe '#validate_raw_role!' do
33
- let(:controller) { ControllerMock.new }
21
+ let(:controller) { ControllerMock(described_class).new }
34
22
  let(:current_user) { instance_double(Cassette::Authentication::User) }
35
23
 
36
24
  before do
@@ -64,7 +52,7 @@ describe Cassette::Authentication::Filter do
64
52
  end
65
53
 
66
54
  describe '#validate_role!' do
67
- let(:controller) { ControllerMock.new }
55
+ let(:controller) { ControllerMock(described_class).new }
68
56
  let(:current_user) { instance_double(Cassette::Authentication::User) }
69
57
 
70
58
  before do
@@ -97,7 +85,6 @@ describe Cassette::Authentication::Filter do
97
85
  end
98
86
  end
99
87
 
100
-
101
88
  describe '#validate_authentication_ticket' do
102
89
  shared_examples_for 'controller without authentication' do
103
90
  it 'does not validate tickets' do
@@ -113,14 +100,14 @@ describe Cassette::Authentication::Filter do
113
100
 
114
101
  it_behaves_like 'with NOAUTH' do
115
102
  context 'and no ticket' do
116
- let(:controller) { ControllerMock.new }
103
+ let(:controller) { ControllerMock(described_class).new }
117
104
 
118
105
  it_behaves_like 'controller without authentication'
119
106
  end
120
107
 
121
108
  context 'and a ticket header' do
122
109
  let(:controller) do
123
- ControllerMock.new({}, 'Service-Ticket' => 'le ticket')
110
+ ControllerMock(described_class).new({}, 'Service-Ticket' => 'le ticket')
124
111
  end
125
112
 
126
113
  it_behaves_like 'controller without authentication'
@@ -128,43 +115,126 @@ describe Cassette::Authentication::Filter do
128
115
 
129
116
  context 'and a ticket param' do
130
117
  let(:controller) do
131
- ControllerMock.new(ticket: 'le ticket')
118
+ ControllerMock(described_class).new(ticket: 'le ticket')
132
119
  end
133
120
 
134
121
  it_behaves_like 'controller without authentication'
135
122
  end
136
123
  end
137
124
 
138
- context 'with a ticket in the query string *AND* headers' do
125
+ context 'when accepts_authentication_service? returns false' do
139
126
  let(:controller) do
140
- ControllerMock.new({ 'ticket' => 'le other ticket' }, 'Service-Ticket' => 'le ticket')
127
+ ControllerMock(described_class).new(ticket: 'le ticket')
141
128
  end
142
129
 
143
- it 'should send only the header ticket to validation' do
144
- controller.validate_authentication_ticket
145
- expect(Cassette::Authentication).to have_received(:validate_ticket).with('le ticket', Cassette.config.service)
130
+ before do
131
+ expect(controller).to receive(:accepts_authentication_service?)
132
+ .with(Cassette.config.service) { false }
133
+ end
134
+
135
+ it 'raises a Cassette::Errors::Forbidden' do
136
+ expect { controller.validate_authentication_ticket }
137
+ .to raise_error(Cassette::Errors::Forbidden)
146
138
  end
147
139
  end
148
140
 
149
- context 'with a ticket in the query string' do
150
- let(:controller) do
151
- ControllerMock.new('ticket' => 'le ticket')
141
+ context 'when accepts_authentication_service? returns true' do
142
+ before do
143
+ expect(controller).to receive(:accepts_authentication_service?).with(anything) { true }
152
144
  end
153
145
 
154
- it 'should send the ticket to validation' do
155
- controller.validate_authentication_ticket
156
- expect(Cassette::Authentication).to have_received(:validate_ticket).with('le ticket', Cassette.config.service)
146
+ context 'with a ticket in the query string *AND* headers' do
147
+ let(:controller) do
148
+ ControllerMock(described_class).new({ 'ticket' => 'le other ticket' },
149
+ 'Service-Ticket' => 'le ticket')
150
+ end
151
+
152
+ it 'should send only the header ticket to validation' do
153
+ controller.validate_authentication_ticket
154
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with('le ticket', Cassette.config.service)
155
+ end
156
+ end
157
+
158
+ context 'with a ticket in the query string' do
159
+ let(:controller) do
160
+ ControllerMock(described_class).new('ticket' => 'le ticket')
161
+ end
162
+
163
+ it 'should send the ticket to validation' do
164
+ controller.validate_authentication_ticket
165
+ expect(Cassette::Authentication).to have_received(:validate_ticket).with('le ticket', Cassette.config.service)
166
+ end
157
167
  end
168
+
169
+ context 'when #authentication_service is overriden' do
170
+ let(:controller) do
171
+ mod = Module.new do
172
+ def authentication_service
173
+ "subdomain.#{Cassette.config.service}"
174
+ end
175
+ end
176
+
177
+ ControllerMock(described_class, mod).new({}, 'Service-Ticket' => 'le ticket')
178
+ end
179
+
180
+ it 'validates with the overriden value and not the config' do
181
+ controller.validate_authentication_ticket
182
+
183
+ expect(Cassette::Authentication).to have_received(:validate_ticket)
184
+ .with('le ticket', "subdomain.#{Cassette.config.service}")
185
+ end
186
+ end
187
+
188
+ context 'with a ticket in the Service-Ticket header' do
189
+ let(:controller) do
190
+ ControllerMock(described_class).new({}, 'Service-Ticket' => 'le ticket')
191
+ end
192
+
193
+ it 'sends the ticket to validation' do
194
+ controller.validate_authentication_ticket
195
+
196
+ expect(Cassette::Authentication).to have_received(:validate_ticket)
197
+ .with('le ticket', Cassette.config.service)
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ describe '#accepts_authentication_service?' do
204
+ let(:controller) do
205
+ ControllerMock(described_class).new(ticket: 'le ticket')
158
206
  end
159
207
 
160
- context 'with a ticket in the Service-Ticket header' do
161
- let(:controller) do
162
- ControllerMock.new({}, 'Service-Ticket' => 'le ticket')
208
+ before do
209
+ allow(Cassette).to receive(:config) { config }
210
+ end
211
+
212
+ subject { controller.accepts_authentication_service?(service) }
213
+
214
+ context 'when config responds to #services' do
215
+ let(:subdomain) { "subdomain.acme.org" }
216
+ let(:not_related) { "acme.org" }
217
+
218
+ let(:config) do
219
+ OpenStruct.new(YAML.load_file('spec/config.yml').merge(services: [subdomain]))
163
220
  end
164
221
 
165
- it 'should send the ticket to validation' do
166
- controller.validate_authentication_ticket
167
- expect(Cassette::Authentication).to have_received(:validate_ticket).with('le ticket', Cassette.config.service)
222
+ context 'and the authentication service is included in the configuration' do
223
+ let(:service) { subdomain }
224
+
225
+ it { is_expected.to eq true }
226
+ end
227
+
228
+ context 'and the authentication service is Cassette.config.service' do
229
+ let(:service) { Cassette.config.service }
230
+
231
+ it { is_expected.to eq true }
232
+ end
233
+
234
+ context 'and the authentication service is not included in the configuration' do
235
+ let(:service) { not_related }
236
+
237
+ it { is_expected.to eq false }
168
238
  end
169
239
  end
170
240
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Cassette::Rubycas::UserFactory do
4
+ let(:mod) do
5
+ Module.new do
6
+ extend Cassette::Rubycas::UserFactory
7
+ extend self
8
+ end
9
+ end
10
+
11
+ describe '#from_session' do
12
+ let(:session) do
13
+ name = Faker.name
14
+
15
+ {
16
+ cas_user: Faker::Internet.user_name(name),
17
+ cas_extra_attributes: {
18
+ email: Faker::Internet.email(name),
19
+ type: 'Customer',
20
+ authorities: '[CASTEST_ADMIN]'
21
+ }
22
+ }
23
+ end
24
+
25
+ let(:attributes) do
26
+ session[:cas_extra_attributes]
27
+ end
28
+
29
+ subject do
30
+ mod.from_session(session)
31
+ end
32
+
33
+ context 'with default attributes' do
34
+ its(:login) { is_expected.to eq(session[:cas_user]) }
35
+ its(:name) { is_expected.to eq(attributes[:name]) }
36
+ its(:email) { is_expected.to eq(attributes[:email]) }
37
+ its(:type) { is_expected.to eq(attributes[:type].downcase) }
38
+ it { is_expected.to be_customer }
39
+ it { is_expected.not_to be_employee }
40
+ end
41
+ end
42
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+
2
2
 
3
3
  describe Cassette::Authentication::User do
4
4
  let(:base_authority) do
@@ -20,13 +20,14 @@ describe Cassette::Authentication::User do
20
20
  expect(config).to receive(:base_authority).and_return('TESTAPI')
21
21
  expect(Cassette::Authentication::Authorities).to receive(:new).with('[CUSTOMERAPI, SAPI]', 'TESTAPI')
22
22
 
23
- Cassette::Authentication::User.new(login: 'john.doe', name: 'John Doe', authorities: '[CUSTOMERAPI, SAPI]', config: config)
23
+ Cassette::Authentication::User.new(login: 'john.doe', name: 'John Doe',
24
+ authorities: '[CUSTOMERAPI, SAPI]', config: config)
24
25
  end
25
26
  end
26
27
  end
27
28
 
28
29
  describe '#has_role?' do
29
- let (:user) do
30
+ let(:user) do
30
31
  Cassette::Authentication::User.new(login: 'john.doe', name: 'John Doe',
31
32
  authorities: "[#{base_authority}, SAPI, #{base_authority}_CREATE-USER]")
32
33
  end