cf-uaac 1.3.4 → 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
data/cf-uaac.gemspec CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
38
38
  s.add_development_dependency "simplecov"
39
39
  s.add_development_dependency "simplecov-rcov"
40
40
  s.add_development_dependency "ci_reporter"
41
- s.add_runtime_dependency "cf-uaa-lib", ">= 1.3.4", "<= 1.3.4"
41
+ s.add_runtime_dependency "cf-uaa-lib", ">= 1.3.6", "<= 1.3.6"
42
42
  s.add_runtime_dependency "highline"
43
43
  s.add_runtime_dependency "eventmachine"
44
44
  s.add_runtime_dependency "launchy"
@@ -47,15 +47,15 @@ class ClientCli < CommonCli
47
47
  end
48
48
  end
49
49
 
50
- desc "clients", "List client registrations" do
51
- pp scim_request { |cr| cr.all_pages(:client) }
50
+ desc "clients [filter]", "List client registrations", :attrs, :start, :count do |filter|
51
+ scim_common_list(:client, filter)
52
52
  end
53
53
 
54
- desc "client get [name]", "Get specific client registration" do |name|
55
- pp scim_request { |cr| cr.get(:client, cr.id(:client, clientname(name))) }
54
+ desc "client get [name]", "Get specific client registration", :attrs do |name|
55
+ pp scim_request { |sr| scim_get_object(sr, :client, clientname(name), opts[:attrs]) }
56
56
  end
57
57
 
58
- define_option :clone, "--clone <other_client>", "get default client settings from existing client"
58
+ define_option :clone, "--clone <other>", "get default settings from other"
59
59
  define_option :interact, "--[no-]interactive", "-i", "interactively verify all values"
60
60
 
61
61
  desc "client add [name]", "Add client registration",
@@ -63,7 +63,7 @@ class ClientCli < CommonCli
63
63
  pp scim_request { |cr|
64
64
  opts[:client_id] = clientname(name)
65
65
  opts[:secret] = verified_pwd("New client secret", opts[:secret])
66
- defaults = opts[:clone] ? Util.hash_keys!(cr.get(opts[:clone]), :sym) : {}
66
+ defaults = opts[:clone] ? Util.hash_keys!(cr.get(:client, opts[:clone]), :sym) : {}
67
67
  defaults.delete(:client_id)
68
68
  cr.add(:client, client_info(defaults))
69
69
  }
data/lib/cli/common.rb CHANGED
@@ -80,6 +80,22 @@ class CommonCli < Topic
80
80
  info
81
81
  end
82
82
 
83
+ def scim_common_list(type, filter)
84
+ pp scim_request { |sr|
85
+ query = { attributes: opts[:attrs], filter: filter }
86
+ opts[:start] || opts[:count] ?
87
+ sr.query(type, query.merge!(startIndex: opts[:start], count: opts[:count])):
88
+ sr.all_pages(type, query)
89
+ }
90
+ end
91
+
92
+ def scim_get_object(scim, type, name, attrs = nil)
93
+ query = { attributes: attrs, filter: "#{scim.name_attr(type)} eq \"#{name}\""}
94
+ info = scim.all_pages(type, query)
95
+ raise BadResponse unless info.is_a?(Array) && info.length < 2
96
+ raise NotFound if info.length == 0
97
+ info[0]
98
+ end
83
99
  end
84
100
 
85
101
  class MiscCli < CommonCli
@@ -141,8 +157,8 @@ class MiscCli < CommonCli
141
157
  update_target_info(info) if info[:prompts]
142
158
  end
143
159
  return say "no target set" unless Config.target
144
- return say "target set to #{Config.target}" unless Config.context
145
- say "target set to #{Config.target}, with context #{Config.context}"
160
+ return say "\nTarget: #{Config.target}\n\n" unless Config.context
161
+ say "\nTarget: #{Config.target}\nContext: #{Config.context}, from client #{Config[:client_id]}\n\n"
146
162
  end
147
163
 
148
164
  desc "targets", "Display all targets" do
data/lib/cli/config.rb CHANGED
@@ -120,6 +120,8 @@ class Config
120
120
  @config[@target][:contexts][@context][attr]
121
121
  end
122
122
 
123
+ def self.[](attr) value(attr) end
124
+
123
125
  def self.delete_attr(attr)
124
126
  raise ArgumentError, "target and context not set" unless @target && @context
125
127
  @config[@target][:contexts][@context].delete(attr)
data/lib/cli/group.rb CHANGED
@@ -24,25 +24,20 @@ class GroupCli < CommonCli
24
24
  def gname(name) name || ask("Group name") end
25
25
 
26
26
  desc "groups [filter]", "List groups", :attrs, :start, :count do |filter|
27
- pp scim_request { |ua|
28
- query = { attributes: opts[:attrs], filter: filter }
29
- opts[:start] || opts[:count] ?
30
- ua.query_groups(query.merge!(startIndex: opts[:start], count: opts[:count])):
31
- ua.all_pages(:group, query)
32
- }
27
+ scim_common_list(:group, filter)
33
28
  end
34
29
 
35
- desc "group get [name]", "Get specific group information" do |name|
36
- pp scim_request { |ua| ua.get(:group, ua.id(:group, gname(name))) }
30
+ desc "group get [name]", "Get specific group information", :attrs do |name|
31
+ pp scim_request { |sr| scim_get_object(sr, :group, gname(name), opts[:attrs]) }
37
32
  end
38
33
 
39
34
  desc "group add [name]", "Adds a group" do |name|
40
- pp scim_request { |ua| ua.add(:group, displayName: gname(name)) }
35
+ pp scim_request { |scim| scim.add(:group, displayName: gname(name)) }
41
36
  end
42
37
 
43
38
  desc "group delete [name]", "Delete group" do |name|
44
- pp scim_request { |ua|
45
- ua.delete(:delete, ua.id(:group, gname(name)))
39
+ pp scim_request { |scim|
40
+ scim.delete(:group, scim.id(:group, gname(name)))
46
41
  "success"
47
42
  }
48
43
  end
@@ -55,31 +50,46 @@ class GroupCli < CommonCli
55
50
  }
56
51
  end
57
52
 
58
- desc "member add [name] [members...]", "add members to a group" do |name, *members|
59
- pp scim_request { |ua|
60
- group = ua.get(:group, ua.id(:group, gname(name)))
61
- old_ids = id_set(group["members"] || [])
62
- new_ids = id_set(ua.ids(:user, *members))
63
- raise "not all members found, none added" unless new_ids.size == members.size
64
- group["members"] = (old_ids + new_ids).to_a
65
- raise "no new members given" unless group["members"].size > old_ids.size
66
- ua.put(:group, group)
53
+ def update_members(scim, name, attr, users, add = true)
54
+ group = scim_get_object(scim, :group, gname(name))
55
+ old_ids = id_set(group[attr] || [])
56
+ new_ids = id_set(scim.ids(:user, *users))
57
+ if add
58
+ raise "not all users found, none added" unless new_ids.size == users.size
59
+ group[attr] = (old_ids + new_ids).to_a
60
+ raise "no new users given" unless group[attr].size > old_ids.size
61
+ else
62
+ raise "not all users found, none deleted" unless new_ids.size == users.size
63
+ group[attr] = (old_ids - new_ids).to_a
64
+ raise "no existing users to delete" unless group[attr].size < old_ids.size
65
+ group.delete(attr) if group[attr].empty?
66
+ end
67
+ scim.put(:group, group)
67
68
  "success"
68
- }
69
69
  end
70
70
 
71
- desc "member delete [name] [members...]", "remove members from a group" do |name, *members|
72
- pp scim_request { |ua|
73
- group = ua.get(:group, ua.id(:group, gname(name)))
74
- old_ids = id_set(group["members"] || [])
75
- new_ids = id_set(ua.ids(:user, *members))
76
- raise "not all members found, none deleted" unless new_ids.size == members.size
77
- group["members"] = (old_ids - new_ids).to_a
78
- raise "no existing members to delete" unless group["members"].size < old_ids.size
79
- group.delete("members") if group["members"].empty?
80
- ua.put(:group, group)
81
- "success"
82
- }
71
+ desc "member add [name] [users...]", "add members to a group" do |name, *users|
72
+ pp scim_request { |scim| update_members(scim, name, "members", users) }
73
+ end
74
+
75
+ desc "member delete [name] [users...]", "remove members from a group" do |name, *users|
76
+ pp scim_request { |scim| update_members(scim, name, "members", users, false) }
77
+ end
78
+
79
+ desc "group reader add [name] [users...]", "add users who can read the members" do |name, *users|
80
+ pp scim_request { |scim| update_members(scim, name, "readers", users) }
81
+ end
82
+
83
+ desc "group reader delete [name] [users...]", "delete users who can read members" do |name, *users|
84
+ pp scim_request { |scim| update_members(scim, name, "readers", users, false) }
85
+ end
86
+
87
+ desc "group writer add [name] [users...]", "add users who can modify group" do |name, *users|
88
+ pp scim_request { |scim| update_members(scim, name, "writers", users) }
89
+ end
90
+
91
+ desc "group writer delete [name] [users...]", "remove user who can modify group" do |name, *users|
92
+ pp scim_request { |scim| update_members(scim, name, "writers", users, false) }
83
93
  end
84
94
 
85
95
  end
data/lib/cli/token.rb CHANGED
@@ -66,7 +66,16 @@ class TokenCli < CommonCli
66
66
  topic "Tokens", "token", "login"
67
67
 
68
68
  def say_success(grant)
69
- say "\nSuccessfully fetched token via a #{grant} grant.\nTarget: #{Config.target}\nContext: #{Config.context}\n"
69
+ say "\nSuccessfully fetched token via #{grant} grant.\nTarget: #{Config.target}\nContext: #{Config.context}, from client #{Config[:client_id]}\n\n"
70
+ end
71
+
72
+ def set_context(token_info)
73
+ return gripe "attempt to get token failed\n" unless token_info && token_info["access_token"]
74
+ contents = TokenCoder.decode(token_info["access_token"], verify: false)
75
+ Config.context = contents["user_name"] || contents["client_id"] || "bad_token"
76
+ Config.add_opts(user_id: contents["user_id"]) if contents["user_id"]
77
+ Config.add_opts(client_id: contents["client_id"]) if contents["client_id"]
78
+ Config.add_opts token_info
70
79
  end
71
80
 
72
81
  def issuer_request(client_id, secret = nil)
@@ -83,7 +92,7 @@ class TokenCli < CommonCli
83
92
  "Gets a token by posting user credentials with an implicit grant request",
84
93
  :client, :scope do |*args|
85
94
  client_name = opts[:client] || "vmc"
86
- token = issuer_request(client_name, "") { |ti|
95
+ reply = issuer_request(client_name, "") { |ti|
87
96
  prompts = ti.prompts
88
97
  creds = {}
89
98
  prompts.each do |k, v|
@@ -99,41 +108,31 @@ class TokenCli < CommonCli
99
108
  end
100
109
  ti.implicit_grant_with_creds(creds, opts[:scope]).info
101
110
  }
102
- return gripe "attempt to get token failed\n" unless token && token["access_token"]
103
- tokinfo = TokenCoder.decode(token["access_token"], verify: false)
104
- Config.context = tokinfo["user_name"]
105
- Config.add_opts(user_id: tokinfo["user_id"])
106
- Config.add_opts token
107
- say_success "implicit (with posted credentials)"
111
+ say_success "implicit (with posted credentials)" if set_context(reply)
108
112
  end
109
113
 
110
114
  define_option :secret, "--secret <secret>", "-s", "client secret"
111
115
  desc "token client get [name]",
112
116
  "Gets a token with client credentials grant", :secret, :scope do |id|
113
- id = clientname(id)
114
- return unless info = issuer_request(id, clientsecret) { |ti|
117
+ reply = issuer_request(clientname(id), clientsecret) { |ti|
115
118
  ti.client_credentials_grant(opts[:scope]).info
116
119
  }
117
- Config.context = id
118
- Config.add_opts info
119
- say_success "client credentials"
120
+ say_success "client credentials" if set_context(reply)
120
121
  end
121
122
 
122
123
  define_option :password, "-p", "--password <password>", "user password"
123
124
  desc "token owner get [client] [user]", "Gets a token with a resource owner password grant",
124
125
  :secret, :password, :scope do |client, user|
125
- return unless info = issuer_request(clientname(client), clientsecret) { |ti|
126
+ reply = issuer_request(clientname(client), clientsecret) { |ti|
126
127
  ti.owner_password_grant(user = username(user), userpwd, opts[:scope]).info
127
128
  }
128
- Config.context = user
129
- Config.add_opts info
130
- say_success "owner password"
129
+ say_success "owner password" if set_context(reply)
131
130
  end
132
131
 
133
132
  desc "token refresh [refreshtoken]", "Gets a new access token from a refresh token", :client, :secret, :scope do |rtok|
134
133
  rtok ||= Config.value(:refresh_token)
135
- Config.add_opts issuer_request(clientname, clientsecret) { |ti| ti.refresh_token_grant(rtok, opts[:scope]).info }
136
- say_success "refresh"
134
+ reply = issuer_request(clientname, clientsecret) { |ti| ti.refresh_token_grant(rtok, opts[:scope]).info }
135
+ say_success "refresh" if set_context(reply)
137
136
  end
138
137
 
139
138
  VMC_TOKEN_FILE = File.join ENV["HOME"], ".vmc_token"
@@ -156,9 +155,7 @@ class TokenCli < CommonCli
156
155
  sleep 5
157
156
  print "."
158
157
  end
159
- Config.context = TokenCoder.decode(catcher.info[:access_token], verify: false)["user_name"]
160
- Config.add_opts catcher.info
161
- say_success secret ? "authorization code" : "implicit"
158
+ say_success(secret ? "authorization code" : "implicit") if set_context(catcher.info)
162
159
  return unless opts[:vmc]
163
160
  begin
164
161
  vmc_target = File.open(VMC_TARGET_FILE, 'r') { |f| f.read.strip }
data/lib/cli/user.rb CHANGED
@@ -39,23 +39,18 @@ class UserCli < CommonCli
39
39
  define_option :start, "--start <number>", "start of output page"
40
40
  define_option :count, "--count <number>", "max number per page"
41
41
  desc "users [filter]", "List user accounts", :attrs, :start, :count do |filter|
42
- pp scim_request { |ua|
43
- query = { attributes: opts[:attrs], filter: filter }
44
- opts[:start] || opts[:count] ?
45
- ua.query(:user, query.merge!(startIndex: opts[:start], count: opts[:count])):
46
- ua.all_pages(:user, query)
47
- }
42
+ scim_common_list(:user, filter)
48
43
  end
49
44
 
50
- desc "user get [name]", "Get specific user account" do |name|
51
- pp scim_request { |ua| ua.get(:user, ua.id(:user, username(name))) }
45
+ desc "user get [name]", "Get specific user account", :attrs do |name|
46
+ pp scim_request { |sr| scim_get_object(sr, :user, username(name), opts[:attrs]) }
52
47
  end
53
48
 
54
49
  desc "user add [name]", "Add a user account", *USER_INFO_OPTS, :password do |name|
55
50
  info = {userName: username(name), password: verified_pwd("Password", opts[:password])}
56
- pp scim_request { |ua|
51
+ pp scim_request { |ua|
57
52
  ua.add(:user, user_opts(info))
58
- "user account successfully added"
53
+ "user account successfully added"
59
54
  }
60
55
  end
61
56
 
@@ -97,6 +92,7 @@ class UserCli < CommonCli
97
92
  define_option :old_password, "-o", "--old_password <password>", "current password"
98
93
  desc "password change", "Change password for authenticated user in current context", :old_password, :password do
99
94
  pp scim_request { |ua|
95
+ raise "no user_id in current context" unless Config.value(:user_id)
100
96
  oldpwd = opts[:old_password] || ask_pwd("Current password")
101
97
  ua.change_password(Config.value(:user_id), verified_pwd("New password", opts[:password]), oldpwd)
102
98
  "password successfully changed"
data/lib/cli/version.rb CHANGED
@@ -14,6 +14,6 @@
14
14
  # Cloud Foundry namespace
15
15
  module CF
16
16
  module UAA
17
- CLI_VERSION = "1.3.4"
17
+ CLI_VERSION = "1.3.6"
18
18
  end
19
19
  end
data/lib/stub/scim.rb CHANGED
@@ -31,8 +31,9 @@ class StubScim
31
31
  READ_ONLY_ATTRS = [:rtype, :id, :meta, :groups].to_set
32
32
  BOOLEANS = [:active].to_set
33
33
  NUMBERS = [:access_token_validity, :refresh_token_validity].to_set
34
- GROUPS = [:groups, :auto_approved_scope, :scope, :authorities].to_set
35
- REFERENCES = [*GROUPS, :members, :owners, :readers].to_set # users or groups
34
+ GROUPS = [:groups, :autoapprove, :scope, :authorities].to_set # values must be group ID
35
+ MEMBERSHIP = [:members, :writers, :readers].to_set # users or groups
36
+ REFERENCES = GROUPS + MEMBERSHIP # users or groups
36
37
  ENUMS = { authorized_grant_types: ["client_credentials", "implicit",
37
38
  "authorization_code", "password", "refresh_token"].to_set }
38
39
  GENERAL_MULTI = [:emails, :phonenumbers, :ims, :photos, :entitlements,
@@ -57,9 +58,9 @@ class StubScim
57
58
  :entitlements, :roles, :x509certificates, :name, :addresses,
58
59
  :authorizations, :groups].to_set,
59
60
  client: [*COMMON_ATTRS, :client_id, :client_secret, :authorities,
60
- :authorized_grant_types, :scope, :auto_approved_scope,
61
+ :authorized_grant_types, :scope, :autoapprove,
61
62
  :access_token_validity, :refresh_token_validity, :redirect_uri].to_set,
62
- group: [*COMMON_ATTRS, :displayname, :members, :owners, :readers].to_set }
63
+ group: [*COMMON_ATTRS, :displayname, :members, :writers, :readers].to_set }
63
64
  VISIBLE_ATTRS = {user: Set.new(LEGAL_ATTRS[:user] - HIDDEN_ATTRS),
64
65
  client: Set.new(LEGAL_ATTRS[:client] - HIDDEN_ATTRS),
65
66
  group: Set.new(LEGAL_ATTRS[:group] - HIDDEN_ATTRS)}
@@ -123,7 +124,7 @@ class StubScim
123
124
  when *NUMBERS then v.is_a?(Integer)
124
125
  when *GENERAL_MULTI then valid_multi?(v, GENERAL_SUBATTRS, true)
125
126
  when *GROUPS then valid_ids?(v, :group)
126
- when *REFERENCES then valid_ids?(v)
127
+ when *MEMBERSHIP then valid_ids?(v)
127
128
  when ENUMS[k] then ENUMS[k].include?(v)
128
129
  when *EXPLICIT_SINGLE.keys then valid_complex?(v, EXPLICIT_SINGLE[k])
129
130
  when *EXPLICIT_MULTI.keys then valid_multi?(v, EXPLICIT_MULTI[k])
@@ -158,7 +159,11 @@ class StubScim
158
159
  attrs.each_with_object({}) {|a, o|
159
160
  next unless thing[a]
160
161
  case a
161
- when *REFERENCES then o[a] = thing[a].to_a
162
+ when *MEMBERSHIP
163
+ o[a] = thing[a].each_with_object([]) { |v, a|
164
+ a << { value: v, type: ref_by_id(v)[:rtype] }
165
+ }
166
+ when *GROUPS then o[a] = thing[a].to_a
162
167
  when *GENERAL_MULTI then o[a] = thing[a].values
163
168
  else o[a] = thing[a]
164
169
  end
@@ -169,10 +174,16 @@ class StubScim
169
174
  members.each {|m| (m[:groups] ||= Set.new) << gid if m = ref_by_id(m, :user)} if members
170
175
  end
171
176
 
172
- def remove_user_groups(gid, members)
177
+ def delete_user_groups(gid, members)
173
178
  members.each {|m| m[:groups].delete(gid) if m = ref_by_id(m, :user) } if members
174
179
  end
175
180
 
181
+ def delete_references(id)
182
+ @things_by_id.each { |k, v|
183
+ REFERENCES.each { |a| v.delete(a) if v[a] && v[a].delete(id) && v[a].empty? }
184
+ }
185
+ end
186
+
176
187
  public
177
188
 
178
189
  def initialize; @things_by_id, @things_by_name = {}, {} end
@@ -208,7 +219,7 @@ class StubScim
208
219
  if new_thing[:members] || thing[:members]
209
220
  old_members = thing[:members] || Set.new
210
221
  new_members = new_thing[:members] || Set.new
211
- remove_user_groups(id, old_members - new_members)
222
+ delete_user_groups(id, old_members - new_members)
212
223
  add_user_groups(id, new_members - old_members)
213
224
  end
214
225
  READ_ONLY_ATTRS.each { |a| new_thing[a] = thing[a] if thing[a] }
@@ -225,18 +236,23 @@ class StubScim
225
236
  add_user_groups(gid, Set[member])
226
237
  end
227
238
 
239
+ def is_member(gid, member, attr = :members)
240
+ (g = ref_by_id(gid, :group)) && (a = g[attr]) && a.include?(member)
241
+ end
242
+
228
243
  def set_hidden_attr(id, attr, value)
229
244
  raise NotFound unless thing = ref_by_id(id)
230
245
  raise ArgumentError unless HIDDEN_ATTRS.include?(attr)
231
246
  thing[attr] = value
232
247
  end
233
248
 
234
- def remove(id, rtype = nil)
249
+ def delete(id, rtype = nil)
235
250
  return unless thing = ref_by_id(id, rtype)
236
251
  rtype = thing[:rtype]
237
- remove_user_groups(id, thing[:members])
252
+ delete_user_groups(id, thing[:members])
238
253
  @things_by_id.delete(id)
239
254
  thing = @things_by_name.delete(rtype.to_s + thing[NAME_ATTR[rtype]].downcase)
255
+ delete_references(id)
240
256
  remove_attrs(output(thing))
241
257
  end
242
258
 
@@ -250,10 +266,12 @@ class StubScim
250
266
  output(thing, attrs)
251
267
  end
252
268
 
253
- def find(rtype, start = 0, count = nil, filter_string = nil, attrs = nil)
254
- filter, total = ScimFilter.new(filter_string), 0
269
+ def find(rtype, opts = {})
270
+ filter, total, start = ScimFilter.new(opts[:filter]), 0, (opts[:start] || 0)
271
+ count, attrs, acl, acl_id = opts[:count], opts[:attrs], opts[:acl], opts[:acl_id]
255
272
  objs = @things_by_id.each_with_object([]) { |(k, v), o|
256
273
  next unless rtype == v[:rtype] && filter.match?(v)
274
+ next if acl && acl_id && !is_member(v[:id], acl_id, acl)
257
275
  o << output(v, attrs) if total >= start && (count.nil? || o.length < count)
258
276
  total += 1
259
277
  }
data/lib/stub/server.rb CHANGED
@@ -176,7 +176,7 @@ class Base
176
176
  request.path.slice!(0..server.root.length - 1)
177
177
  end
178
178
  @match, handler = self.class.find_route(request)
179
- server.logger.debug "processing request to path #{request.path} for route #{@match ? @match.regexp : 'default'}"
179
+ server.logger.debug "processing #{request.method} to path #{request.path}"
180
180
  send handler
181
181
  reply.headers['connection'] ||= request.headers['connection'] if request.headers['connection']
182
182
  server.logger.debug "replying to path #{request.path} with #{reply.body.length} bytes of #{reply.headers['content-type']}"
data/lib/stub/uaa.rb CHANGED
@@ -30,23 +30,22 @@ class StubUAAConn < Stub::Base
30
30
  end
31
31
  end
32
32
 
33
- def valid_token(required_scope)
34
- return nil unless (ah = request.headers["authorization"]) && (ah = ah.split(' '))[0] =~ /^bearer$/i
35
- contents = TokenCoder.decode(ah[1], accept_algorithms: "none")
36
- contents["scope"], required_scope = Util.arglist(contents["scope"]), Util.arglist(required_scope)
37
- return contents if required_scope.nil? || !(required_scope & contents["scope"]).empty?
38
- reply_in_kind(403, error: "insufficient_scope",
39
- error_description: "required scope #{Util.strlist(required_scope)}")
40
- nil
41
- end
42
-
33
+ def bad_request(msg = nil); reply_in_kind(400, error: "bad request#{msg ? ',' : ''} #{msg}") end
34
+ def not_found(name = nil); reply_in_kind(404, error: "#{name} not found") end
35
+ def access_denied(msg = "access denied") reply_in_kind(403, error: "access_denied", error_description: msg) end
43
36
  def ids_to_names(ids); ids ? ids.map { |id| server.scim.name(id) } : [] end
44
37
  def names_to_ids(names, rtype); names ? names.map { |name| server.scim.id(name, rtype) } : [] end
45
- def bad_request(message = nil); reply_in_kind(400, error: "bad request#{message ? ',' : ''} #{message}") end
46
- def not_found(name = nil); reply_in_kind(404, error: "#{name} not found") end
47
38
  def encode_cookie(obj = {}) Util.json_encode64(obj) end
48
39
  def decode_cookie(str) Util.json.decode64(str) end
49
40
 
41
+ def valid_token(accepted_scope)
42
+ return nil unless (ah = request.headers["authorization"]) && (ah = ah.split(' '))[0] =~ /^bearer$/i
43
+ contents = TokenCoder.decode(ah[1], accept_algorithms: "none")
44
+ contents["scope"], accepted_scope = Util.arglist(contents["scope"]), Util.arglist(accepted_scope)
45
+ return contents if accepted_scope.nil? || !(accepted_scope & contents["scope"]).empty?
46
+ access_denied("accepted scope #{Util.strlist(accepted_scope)}")
47
+ end
48
+
50
49
  def primary_email(emails)
51
50
  return unless emails
52
51
  emails.each {|e| return e[:value] if e[:type] && e[:type] == "primary"}
@@ -325,12 +324,12 @@ class StubUAAConn < Stub::Base
325
324
  # client endpoints
326
325
  #
327
326
  def client_to_scim(info)
328
- ['authorities', 'scope', 'auto_approve_scope'].each { |a| info[a] = names_to_ids(info[a], :group) if info.key?(a) }
327
+ ['authorities', 'scope', 'autoapprove'].each { |a| info[a] = names_to_ids(info[a], :group) if info.key?(a) }
329
328
  info
330
329
  end
331
330
 
332
331
  def scim_to_client(info)
333
- [:authorities, :scope, :auto_approve_scope].each { |a| info[a] = ids_to_names(info[a]) if info.key?(a) }
332
+ [:authorities, :scope, :autoapprove].each { |a| info[a] = ids_to_names(info[a]) if info.key?(a) }
334
333
  info.delete(:id)
335
334
  info
336
335
  end
@@ -362,7 +361,7 @@ class StubUAAConn < Stub::Base
362
361
 
363
362
  route :delete, %r{^/oauth/clients/([^/]+)$} do
364
363
  return unless valid_token("clients.write")
365
- return not_found(match[1]) unless server.scim.remove(server.scim.id(match[1], :client))
364
+ return not_found(match[1]) unless server.scim.delete(server.scim.id(match[1], :client))
366
365
  end
367
366
 
368
367
  route :put, %r{^/oauth/clients/([^/]+)/secret$}, "content-type" => %r{application/json} do
@@ -390,11 +389,25 @@ class StubUAAConn < Stub::Base
390
389
  reply_in_kind server.scim.get(id, rtype, *StubScim::VISIBLE_ATTRS[rtype])
391
390
  end
392
391
 
392
+ def obj_access?(rtype, oid, perm)
393
+ major_scope = perm == :writers ? "scim.write" : "scim.read"
394
+ return unless tkn = valid_token("#{major_scope} scim.me")
395
+ return tkn if tkn["scope"].include?(major_scope) ||
396
+ rtype == :group && server.scim.is_member(oid, tkn["user_id"], perm)
397
+ access_denied
398
+ end
399
+
393
400
  route :put, %r{^/(Users|Groups)/([^/]+)$}, "content-type" => %r{application/json} do
394
- return unless valid_token("scim.write")
395
401
  rtype = match[1] == "Users"? :user : :group
396
- id = server.scim.update(match[2], Util.json_parse(request.body, :down), request.headers[:match_if], rtype)
397
- reply_in_kind server.scim.get(id, rtype, *StubScim::VISIBLE_ATTRS[rtype])
402
+ return unless obj_access?(rtype, match[2], :writers)
403
+ version = request.headers['if-match']
404
+ version = version.to_i if version.to_i.to_s == version
405
+ begin
406
+ id = server.scim.update(match[2], Util.json_parse(request.body, :down), version, rtype)
407
+ reply_in_kind server.scim.get(id, rtype, *StubScim::VISIBLE_ATTRS[rtype])
408
+ rescue BadVersion; reply_in_kind(409, error: "invalid object version")
409
+ rescue NotFound; not_found(match[2])
410
+ end
398
411
  end
399
412
 
400
413
  def sanitize_int(arg, default, min, max = nil)
@@ -403,7 +416,7 @@ class StubUAAConn < Stub::Base
403
416
  max && i > max ? max : i
404
417
  end
405
418
 
406
- def page_query(rtype, query, attrs)
419
+ def page_query(rtype, query, attrs, acl = nil, acl_id = nil)
407
420
  if query['attributes']
408
421
  attrs = attrs & Util.arglist(query['attributes']).each_with_object([]) {|a, o|
409
422
  o << a.to_sym if StubScim::ATTR_NAMES.include?(a = a.downcase)
@@ -412,26 +425,33 @@ class StubUAAConn < Stub::Base
412
425
  start = sanitize_int(query['startindex'], 1, 1)
413
426
  count = sanitize_int(query['count'], 15, 1, 3000)
414
427
  return bad_request("invalid startIndex or count") unless start && count
415
- info, total = server.scim.find(rtype, start - 1, count, query['filter'], attrs)
428
+ info, total = server.scim.find(rtype, start: start - 1, count: count,
429
+ filter: query['filter'], attrs: attrs, acl: acl, acl_id: acl_id)
416
430
  reply_in_kind(resources: info, itemsPerPage: info.length, startIndex: start, totalResults: total)
417
431
  end
418
432
 
419
433
  route :get, %r{^/(Users|Groups)(\?|$)(.*)} do
420
- return unless valid_token("scim.read")
421
434
  rtype = match[1] == "Users"? :user : :group
422
- page_query(rtype, Util.decode_form(match[3], :down), StubScim::VISIBLE_ATTRS[rtype])
435
+ return unless tkn = valid_token("scim.read scim.me")
436
+ acl = acl_id = nil
437
+ unless tkn["scope"].include?("scim.read")
438
+ acl, acl_id = :readers, tkn["user_id"]
439
+ return access_denied unless rtype == :group && acl_id
440
+ end
441
+ page_query(rtype, Util.decode_form(match[3], :down),
442
+ StubScim::VISIBLE_ATTRS[rtype], acl, acl_id)
423
443
  end
424
444
 
425
445
  route :get, %r{^/(Users|Groups)/([^/]+)$} do
426
- return unless valid_token("scim.read")
427
446
  rtype = match[1] == "Users"? :user : :group
447
+ return unless obj_access?(rtype, match[2], :readers)
428
448
  return not_found(match[2]) unless obj = server.scim.get(match[2], rtype, *StubScim::VISIBLE_ATTRS[rtype])
429
449
  reply_in_kind(obj)
430
450
  end
431
451
 
432
452
  route :delete, %r{^/(Users|Groups)/([^/]+)$} do
433
453
  return unless valid_token("scim.write")
434
- not_found(match[2]) unless server.scim.remove(match[2], match[1] == "Users"? :user : :group)
454
+ not_found(match[2]) unless server.scim.delete(match[2], match[1] == "Users"? :user : :group)
435
455
  end
436
456
 
437
457
  route :put, %r{^/Users/([^/]+)/password$}, "content-type" => %r{application/json} do
@@ -465,7 +485,7 @@ class StubUAA < Stub::Server
465
485
  @scim = StubScim.new
466
486
  @auto_groups = ["password.write", "openid"]
467
487
  .each_with_object([]) { |g, o| o << @scim.add(:group, 'displayname' => g) }
468
- ["scim.read", "scim.write", "uaa.resource"]
488
+ ["scim.read", "scim.write", "scim.me", "uaa.resource"]
469
489
  .each { |g| @scim.add(:group, 'displayname' => g) }
470
490
  gids = ["clients.write", "clients.read", "clients.secret", "uaa.admin"]
471
491
  .each_with_object([]) { |s, o| o << @scim.add(:group, 'displayname' => s) }
@@ -56,7 +56,7 @@ describe ClientCli do
56
56
 
57
57
  it "fails to create a user account as test client" do
58
58
  Cli.run("user add #{@test_user} -p #{@test_pwd}").should be_nil
59
- Cli.output.string.should include "insufficient_scope"
59
+ Cli.output.string.should include "access_denied"
60
60
  end
61
61
 
62
62
  context "as updated client" do
@@ -73,7 +73,7 @@ describe ClientCli do
73
73
  it "fails to create a user account with old token" do
74
74
  Cli.run("context #{@test_client}").should be
75
75
  Cli.run("user add #{@test_user} -p #{@test_pwd}").should be_nil
76
- Cli.output.string.should include "insufficient_scope"
76
+ Cli.output.string.should include "access_denied"
77
77
  end
78
78
 
79
79
  it "creates a user account with a new token" do
@@ -81,7 +81,7 @@ describe ClientCli do
81
81
  Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
82
82
  Cli.run("token decode")
83
83
  Cli.run("user add #{@test_user.capitalize} -p #{@test_pwd} --email #{@test_user}@example.com --family_name #{@test_user.capitalize} --given_name joe").should be
84
- Cli.output.string.should_not include "insufficient_scope"
84
+ Cli.output.string.should_not include "access_denied"
85
85
  Cli.run("user get #{@test_user}").should be
86
86
  Cli.output.string.should include @test_user.capitalize
87
87
  end
@@ -89,15 +89,15 @@ describe ClientCli do
89
89
 
90
90
  end
91
91
 
92
- context "as admin client" do
93
- it "deletes a client registration" do
94
- client = @test_client.dup
95
- @test_client.replace("")
96
- Cli.run("context #{@admin_client}").should be
97
- Cli.run("client delete #{client}").should be
98
- Cli.output.string.should include "deleted"
99
- end
100
- end
92
+ # context "as admin client" do
93
+ # it "deletes a client registration" do
94
+ # client = @test_client.dup
95
+ # @test_client.replace("")
96
+ # Cli.run("context #{@admin_client}").should be
97
+ # Cli.run("client delete #{client}").should be
98
+ # Cli.output.string.should include "deleted"
99
+ # end
100
+ # end
101
101
 
102
102
  end
103
103
 
data/spec/group_spec.rb CHANGED
@@ -25,67 +25,141 @@ describe GroupCli do
25
25
  Cli.configure("", nil, StringIO.new, true)
26
26
  setup_target(authorities: "clients.read,scim.read,scim.write,uaa.admin")
27
27
  Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
28
- @test_user, @test_pwd = "sam_#{Time.now.to_i}", "correcthorsebatterystaple"
28
+ @test_user, @test_pwd = "SaM_#{Time.now.to_i}_", "correcthorsebatterystaple"
29
29
  @test_group = "JaNiToRs_#{Time.now.to_i}"
30
+ @users = ["w", "r", "m", "n"].map { |v| @test_user + v }
31
+ 5.times { |i| @users << @test_user + i.to_s }
32
+ @users.each { |u| Cli.run("user add #{u} -p #{@test_pwd} --email sam@example.com").should be }
33
+ Cli.run("group add #{@test_group}").should be
34
+ Cli.run("groups -a displayName").should be
35
+ Cli.output.string.should include @test_group
30
36
  end
31
37
 
32
- after :all do cleanup_target end
33
- before :each do Cli.output.string = "" end
38
+ after :all do
39
+ Cli.run "context #{@test_client}"
40
+ @users.each { |u| Cli.run("user delete #{u}") }
41
+ @users.each { |u| Cli.run("user get #{u}").should be_nil }
42
+ Cli.run("group delete #{@test_group}").should be
43
+ cleanup_target
44
+ end
34
45
 
46
+ # actual user and group creation happens in before_all
35
47
  it "creates many users and a group as the test client" do
36
- Cli.run "context #{@test_client}"
37
- Cli.run("user add #{@test_user.upcase} -p #{@test_pwd} " +
38
- "--email joey@example.com --family_name JONES --given_name JOE").should be
39
- 29.times { |i| Cli.run("user add #{@test_user.capitalize}-#{i} -p #{@test_pwd} " +
40
- "--email #{@test_user}+#{i}@example.com " +
41
- "--family_name #{@test_user.capitalize} --given_name joe").should be }
42
- Cli.run("group add #{@test_group}").should be
48
+ @users.each { |u|
49
+ Cli.run("user get #{u}").should be
50
+ Cli.output.string.should include u
51
+ }
52
+ @users.each { |u| Cli.run("member add scim.me #{u}").should be }
43
53
  Cli.run("groups -a displayName").should be
44
54
  Cli.output.string.should include @test_group
55
+ Cli.run("group get #{@test_group.upcase}").should be
56
+ Cli.output.string.should include @test_group
57
+ pending "real uaa can't add members to scim.read group yet" unless @stub_uaa
58
+ Cli.run("member add scim.read #{@test_user}w").should be
45
59
  end
46
60
 
47
61
  it "gets attributes with case-insensitive attribute names" do
48
- Cli.run("groups -a displayname").should be
62
+ Cli.run("groups -a dISPLAYNAME").should be
49
63
  Cli.output.string.should include @test_group
50
64
  end
51
65
 
52
66
  it "lists all users" do
53
67
  Cli.run("users -a UsernamE").should be
54
- 29.times { |i| Cli.output.string.should =~ /#{@test_user.capitalize}-#{i}/i }
55
- end
56
-
57
- it "preserves case in names" do
58
- Cli.run("users -a username").should be
59
- 29.times { |i| Cli.output.string.should =~ /#{@test_user.capitalize}-#{i}/ }
68
+ @users.each { |u| Cli.output.string.should include u }
60
69
  end
61
70
 
62
71
  it "lists a page of users" do
63
- Cli.run("users -a userName --count 13 --start 5").should be
64
- Cli.output.string.should match /itemsPerPage: 13/i
72
+ Cli.run("users -a userName --count 4 --start 5").should be
73
+ Cli.output.string.should match /itemsPerPage: 4/i
65
74
  Cli.output.string.should match /startIndex: 5/i
66
75
  end
67
76
 
77
+ it "adds one user to the group" do
78
+ Cli.run("member add #{@test_group} #{@users[0]}").should be
79
+ Cli.output.string.should include "success"
80
+ end
81
+
68
82
  it "adds users to the group" do
69
83
  cmd = "member add #{@test_group}"
70
- 29.times { |i| cmd << " #{@test_user.capitalize}-#{i}" }
84
+ @users.each { |u| cmd << " #{u.upcase}" }
71
85
  Cli.run(cmd).should be
72
86
  Cli.output.string.should include "success"
73
87
  end
74
88
 
75
- it "adds one user to the group" do
76
- Cli.run("member add #{@test_group} #{@test_user}").should be
89
+ def check_members
90
+ ids = Cli.output.string.scan(/.*value:\s+([^\s]+)/).flatten
91
+ ids.size.should == @users.size
92
+ @users.each { |u|
93
+ Cli.run("user get #{u} -a id").should be
94
+ Cli.output.string =~ /.*id:\s+([^\s]+)/
95
+ ids.delete($1).should == $1
96
+ }
97
+ ids.should be_empty
98
+ end
99
+
100
+ it "lists all group members" do
101
+ Cli.run("group get #{@test_group} -a memBers").should be
102
+ check_members
103
+ end
104
+
105
+ it "adds one reader to the group" do
106
+ Cli.run("group reader add #{@test_group} #{@test_user}r").should be
107
+ Cli.output.string.should include "success"
108
+ end
109
+
110
+ it "adds one writer to the group" do
111
+ Cli.run("group writer add #{@test_group} #{@test_user}w").should be
112
+ Cli.run("group reader add #{@test_group} #{@test_user}w").should be
77
113
  Cli.output.string.should include "success"
78
114
  end
79
115
 
116
+ it "gets readers and writers in the group" do
117
+ Cli.run("group get #{@test_group}").should be
118
+ Cli.output.string.should be
119
+ #puts Cli.output.string
120
+ end
121
+
122
+ it "reads members as a reader" do
123
+ Cli.run("token owner get #{@test_client} -s #{@test_secret} #{@test_user}r -p #{@test_pwd}").should be
124
+ Cli.run("group get #{@test_group} -a memBers").should be
125
+ ids = Cli.output.string.scan(/.*value:\s+([^\s]+)/).flatten
126
+ @users.size.should == ids.size
127
+ end
128
+
129
+ it "can't write members as a reader" do
130
+ pending "real uaa can't search for groups by name by scim.me/readers" unless @stub_uaa
131
+ Cli.run("token owner get #{@test_client} -s #{@test_secret} #{@test_user}r -p #{@test_pwd}").should be
132
+ Cli.run("member add #{@test_group} #{@test_user}z").should_not be
133
+ Cli.output.string.should include "access_denied"
134
+ end
135
+
136
+ it "adds a member as a writer" do
137
+ Cli.run "context #{@test_client}"
138
+ Cli.run("user add #{@test_user}z -p #{@test_pwd} --email sam@example.com").should be
139
+ @users << "#{@test_user}z"
140
+ Cli.run("token owner get #{@test_client} -s #{@test_secret} #{@test_user}w -p #{@test_pwd}").should be
141
+ Cli.run("member add #{@test_group} #{@test_user}z").should be
142
+ Cli.run("group get #{@test_group} -a memBers").should be
143
+ ids = Cli.output.string.scan(/.*value:\s+([^\s]+)/).flatten
144
+ @users.size.should == ids.size
145
+ # check_members
146
+ end
147
+
148
+ it "can't read members as a non-reader" do
149
+ pending "real uaa still returns members even if user is not in readers list" unless @stub_uaa
150
+ Cli.run("token owner get #{@test_client} -s #{@test_secret} #{@test_user}m -p #{@test_pwd}").should be
151
+ Cli.run("group get #{@test_group}").should be_nil
152
+ Cli.output.string.should include "NotFound"
153
+ end
154
+
80
155
  it "deletes all members from a group" do
81
- pending "waiting on bug fix in uaa, [40594865]" if ENV["UAA_CLIENT_TARGET"]
82
- cmd = "member delete #{@test_group} #{@test_user.capitalize}"
83
- 29.times { |i| cmd << " #{@test_user.capitalize}-#{i}" }
156
+ Cli.run "context #{@test_client}"
157
+ cmd = "member delete #{@test_group.downcase} "
158
+ @users.each { |u| cmd << " #{u.downcase}" }
84
159
  Cli.run(cmd).should be
85
160
  Cli.output.string.should include "success"
86
- # and they should really be gone
87
161
  Cli.run("group get #{@test_group}")
88
- Cli.output.string.should_not match /members/i
162
+ Cli.output.string.should_not match /members/i # they should really be gone
89
163
  end
90
164
 
91
165
  end
data/spec/spec_helper.rb CHANGED
@@ -42,8 +42,7 @@ module SpecHelper
42
42
  # restriction that the given block cannot include rspec matchers.
43
43
  def frequest(on_fiber, &blk)
44
44
  return capture_exception(&blk) unless on_fiber
45
- result = nil
46
- cthred = Thread.current
45
+ result, cthred = nil, Thread.current
47
46
  EM.schedule { Fiber.new { result = capture_exception(&blk); cthred.run }.resume }
48
47
  Thread.stop
49
48
  result
@@ -51,8 +50,9 @@ module SpecHelper
51
50
 
52
51
  def setup_target(opts = {})
53
52
  opts = { authorities: "clients.read,scim.read,scim.write,uaa.resource",
54
- grant_types: "client_credentials,password",
55
- scope: "openid,password.write"}.update(opts)
53
+ grant_types: "client_credentials,password,refresh_token",
54
+ scope: "openid,password.write,scim.me,scim.read",
55
+ autoapprove: "openid,password.write,scim.me,scim.read"}.update(opts)
56
56
  @admin_client = ENV["UAA_CLIENT_ID"] || "admin"
57
57
  @admin_secret = ENV["UAA_CLIENT_SECRET"] || "adminsecret"
58
58
  if ENV["UAA_CLIENT_TARGET"]
@@ -68,12 +68,16 @@ module SpecHelper
68
68
  @test_secret = "+=tEsTsEcRet~!@"
69
69
  Cli.run("client add #{test_client} -s #{@test_secret} " +
70
70
  "--authorities #{opts[:authorities]} --scope #{opts[:scope]} " +
71
- "--authorized_grant_types #{opts[:grant_types]}").should be
71
+ "--authorized_grant_types #{opts[:grant_types]} " +
72
+ "--autoapprove #{opts[:autoapprove]}").should be
72
73
  opts.each { |k, a| Util.arglist(a).each {|v| Cli.output.string.should include(v) }}
73
74
  @test_client = test_client
74
75
  end
75
76
 
76
77
  def cleanup_target
78
+ #Cli.run "context #{@test_client}"
79
+ #Cli.run("groups"); puts Cli.output.string
80
+ #Cli.run("users"); puts Cli.output.string
77
81
  Cli.run("context #{@admin_client}")
78
82
  if @test_client && !@test_client.empty?
79
83
  Cli.run("client delete #{@test_client}").should be
data/spec/token_spec.rb CHANGED
@@ -26,14 +26,19 @@ describe TokenCli do
26
26
  setup_target(authorities: "clients.read,scim.read,scim.write,uaa.resource")
27
27
  Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
28
28
  Config.yaml.should include("access_token")
29
- @test_pwd = "TesTpwd$%^"
30
- @test_user = "tEst_UseR+-#{Time.now.to_i}"
29
+ @test_pwd = "@~`!$@%#%^$^&*)(|}{[]\":';?><,./"
30
+ @test_user = "test_user_#{Time.now.to_i}"
31
31
  Cli.run("user add #{@test_user} -p #{@test_pwd} " +
32
32
  "--emails sam@example.com,joNES@sample.com --given_name SamueL " +
33
33
  "--phones 801-555-1212 --family_name jonES").should be
34
34
  end
35
35
 
36
- after :all do cleanup_target end
36
+ after :all do
37
+ Cli.run "context #{@test_client}"
38
+ Cli.run("user delete #{@test_user}").should be
39
+ Cli.run("user get #{@test_user}").should be_nil
40
+ cleanup_target
41
+ end
37
42
 
38
43
  it "logs in with implicit grant & posted credentials as a user" do
39
44
  Cli.run("token get #{@test_user} #{@test_pwd}").should be
data/spec/user_spec.rb CHANGED
@@ -28,12 +28,15 @@ describe UserCli do
28
28
  Config.yaml.should include("access_token")
29
29
  @test_pwd = "TesTpwd$%^"
30
30
  @test_user = "tEst_UseR_#{Time.now.to_i}"
31
- Cli.run("user add #{@test_user} -p #{@test_pwd} " +
31
+ Cli.run("user add #{@test_user} -p #{@test_pwd} " +
32
32
  "--emails sam@example.com,joNES@sample.com --given_name SamueL " +
33
33
  "--phones 801-555-1212 --family_name jonES").should be
34
34
  end
35
35
 
36
- after :all do cleanup_target end
36
+ after :all do
37
+ Cli.run("user delete #{@test_user}")
38
+ cleanup_target
39
+ end
37
40
 
38
41
  it "creates a user" do
39
42
  Cli.output.string.should include "success"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cf-uaac
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.4
4
+ version: 1.3.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-01-05 00:00:00.000000000 Z
16
+ date: 2013-01-29 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: bundler
@@ -118,10 +118,10 @@ dependencies:
118
118
  requirements:
119
119
  - - ! '>='
120
120
  - !ruby/object:Gem::Version
121
- version: 1.3.4
121
+ version: 1.3.6
122
122
  - - <=
123
123
  - !ruby/object:Gem::Version
124
- version: 1.3.4
124
+ version: 1.3.6
125
125
  type: :runtime
126
126
  prerelease: false
127
127
  version_requirements: !ruby/object:Gem::Requirement
@@ -129,10 +129,10 @@ dependencies:
129
129
  requirements:
130
130
  - - ! '>='
131
131
  - !ruby/object:Gem::Version
132
- version: 1.3.4
132
+ version: 1.3.6
133
133
  - - <=
134
134
  - !ruby/object:Gem::Version
135
- version: 1.3.4
135
+ version: 1.3.6
136
136
  - !ruby/object:Gem::Dependency
137
137
  name: highline
138
138
  requirement: !ruby/object:Gem::Requirement
@@ -282,7 +282,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
282
282
  version: '0'
283
283
  segments:
284
284
  - 0
285
- hash: 2590273810398717520
285
+ hash: 4530157202147700966
286
286
  required_rubygems_version: !ruby/object:Gem::Requirement
287
287
  none: false
288
288
  requirements:
@@ -291,7 +291,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
291
291
  version: '0'
292
292
  segments:
293
293
  - 0
294
- hash: 2590273810398717520
294
+ hash: 4530157202147700966
295
295
  requirements: []
296
296
  rubyforge_project: cf-uaac
297
297
  rubygems_version: 1.8.23