class CF::UAA::StubScim

StubScim is the in-memory database of the stubbed out UAA server. Although called StubScim it manages ALL of the objects of the server; users, groups, clients, zones, providers, etc

Constants

ATTR_NAMES
BOOLEANS
COMMON_ATTRS
CREATOR
ENUMS
EXPLICIT_MULTI
EXPLICIT_SINGLE

represents the schema of the scimuser name and meta attributes

GENERAL_MULTI
GENERAL_SUBATTRS
GROUPS
HIDDEN_ATTRS

attribute types. Anything not listed is case-ignore string

MEMBERSHIP
NAME_ATTR

resource class definitions: naming and legal attributes

NUMBERS
READ_ONLY_ATTRS
REFERENCES
SUBATTR_NAMES
VISIBLE_ATTRS

Public Class Methods

new() click to toggle source
# File lib/uaa/stub/scim.rb, line 197
def initialize
  @things_by_id, @things_by_name, @clients_metadata = {}, {}, {}
end

Private Class Methods

remove_hidden(attrs = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 82
def self.remove_hidden(attrs = nil) attrs - HIDDEN_ATTRS if attrs end
searchable_attribute(attr) click to toggle source
# File lib/uaa/stub/scim.rb, line 83
def self.searchable_attribute(attr)
  attr if ATTR_NAMES.include?(attr) && !HIDDEN_ATTRS.include?(attr = attr.to_sym)
end

Public Instance Methods

add(rtype, stuff) click to toggle source
# File lib/uaa/stub/scim.rb, line 207
def add(rtype, stuff)
  unless stuff.is_a?(Hash) && (name = stuff[NAME_ATTR[rtype].to_s])
    raise SchemaViolation, "new #{rtype} has no name #{NAME_ATTR[rtype]}"
  end
  name = append_origin_to_username(name, rtype, stuff['origin'])
  raise AlreadyExists if @things_by_name.key?(name = rtype.to_s + name.downcase)
  enforce_schema(rtype, stuff)
  thing = input(stuff).merge!(rtype: rtype, id: (id = SecureRandom.uuid),
      meta: { created: Time.now.iso8601, last_modified: Time.now.iso8601, version: 1})
  if rtype == :client
    @clients_metadata[stuff['client_id']] = {:createdby => CREATOR}
  end
  add_user_groups(id, thing[:members])
  @things_by_id[id] = @things_by_name[name] = thing
  id
end
add_group_mapping(external_group, group_id, group_name, origin) click to toggle source
# File lib/uaa/stub/scim.rb, line 344
def add_group_mapping(external_group, group_id, group_name, origin)
  group = group_id ? ref_by_id(group_id, :group) : ref_by_name(group_name, :group)
  return unless group
  (group[:external_groups] ||= Hash.new)
  group[:external_groups][external_group] = origin
  group
end
add_member(gid, member) click to toggle source
# File lib/uaa/stub/scim.rb, line 281
def add_member(gid, member)
  return unless g = ref_by_id(gid, :group)
  (g[:members] ||= Set.new) << member
  add_user_groups(gid, Set[member])
end
append_origin_to_username(name, rtype, origin) click to toggle source
# File lib/uaa/stub/scim.rb, line 324
def append_origin_to_username(name, rtype, origin)
  if rtype == :user
    origin =  origin || 'uaa'
    name = "#{name}_#{origin}"
  end
  name
end
delete(id, rtype = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 297
def delete(id, rtype = nil)
  return unless thing = ref_by_id(id, rtype)
  rtype = thing[:rtype]
  delete_user_groups(id, thing[:members])
  origin = @things_by_id[id][:origin]
  @things_by_id.delete(id)
  name = append_origin_to_username(rtype.to_s + thing[NAME_ATTR[rtype]].downcase, rtype, origin)
  thing = @things_by_name.delete(name)
  delete_references(id)
  remove_attrs(output(thing))
end
delete_group_mapping(group_id, external_group, origin) click to toggle source
# File lib/uaa/stub/scim.rb, line 352
def delete_group_mapping(group_id, external_group, origin)
  raise NotFound unless group = ref_by_id(group_id, :group)
  raise NotFound unless group[:external_groups] && group[:external_groups].include?(external_group)
  group[:external_groups][external_group].delete(origin)
end
find(rtype, opts = {}) click to toggle source
# File lib/uaa/stub/scim.rb, line 332
def find(rtype, opts = {})
  filter, total, start = ScimFilter.new(opts[:filter]), 0, (opts[:start] || 0)
  count, attrs, acl, acl_id = opts[:count], opts[:attrs], opts[:acl], opts[:acl_id]
  objs = @things_by_id.each_with_object([]) { |(k, v), o|
    next unless rtype == v[:rtype] && filter.match?(v)
    next if acl && acl_id && !is_member(v[:id], acl_id, acl)
    o << output(v, attrs) if total >= start && (count.nil? || o.length < count)
    total += 1
  }
  [objs, total]
end
get(id, rtype = nil, *attrs) click to toggle source
# File lib/uaa/stub/scim.rb, line 309
def get(id, rtype = nil, *attrs)
  return unless thing = ref_by_id(id, rtype)
  output(thing, attrs)
end
get_by_name(name, rtype, *attrs) click to toggle source
# File lib/uaa/stub/scim.rb, line 318
def get_by_name(name, rtype, *attrs)
  name = append_origin_to_username(name, rtype, nil)
  return unless thing = ref_by_name(name, rtype)
  output(thing, attrs)
end
get_client_meta(client_id) click to toggle source
# File lib/uaa/stub/scim.rb, line 314
def get_client_meta(client_id)
  @clients_metadata[client_id]
end
get_group_mappings() click to toggle source
# File lib/uaa/stub/scim.rb, line 358
def get_group_mappings
  group_mappings = []
  @things_by_id.each do |id, thing|
    if thing[:rtype] == :group
      thing[:external_groups].each do |key, value|
        group_mappings << { groupid: thing[:id], displayname: thing[:displayname], externalgroup: key, origin: value }
      end if thing[:external_groups]
    end
  end unless @things_by_id.empty?
  group_mappings
end
id(name, rtype) click to toggle source
# File lib/uaa/stub/scim.rb, line 202
def id(name, rtype)
  name = append_origin_to_username(name, rtype, nil)
  (t = ref_by_name(name, rtype))? t[:id] : nil
end
is_member(gid, member, attr = :members) click to toggle source
# File lib/uaa/stub/scim.rb, line 287
def is_member(gid, member, attr = :members)
  (g = ref_by_id(gid, :group)) && (a = g[attr]) && a.include?(member)
end
name(id, rtype = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 201
def name(id, rtype = nil) (t = ref_by_id(id, rtype))? t[NAME_ATTR[t[:rtype]]]: nil end
patch(id, stuff, match_version = nil, match_type = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 252
def patch(id, stuff, match_version = nil, match_type = nil)
  raise NotFound unless thing = ref_by_id(id, match_type)
  raise BadVersion if match_version && match_version != thing[:meta][:version]
  enforce_schema(rtype = thing[:rtype], remove_attrs(stuff, READ_ONLY_ATTRS))
  new_thing = input(stuff)
  if newname = new_thing[NAME_ATTR[rtype]]
    oldname = rtype.to_s + thing[NAME_ATTR[rtype]].downcase
    unless (newname = rtype.to_s + newname.downcase) == oldname
      raise AlreadyExists if @things_by_name.key?(newname)
      @things_by_name.delete(oldname)
      @things_by_name[newname] = thing
    end
  end
  if new_thing[:members] || thing[:members]
    old_members = thing[:members] || Set.new
    new_members = new_thing[:members] || Set.new
    delete_user_groups(id, old_members - new_members)
    add_user_groups(id, new_members - old_members)
  end
  READ_ONLY_ATTRS.each { |a| new_thing[a] = thing[a] if thing[a] }
  HIDDEN_ATTRS.each { |a| new_thing[a] = thing[a] if thing[a] }
  new_thing.each do |key, value|
    thing[key] = value
  end
  thing[:meta][:version] += 1
  thing[:meta][:lastmodified] == Time.now.iso8601
  id
end
set_hidden_attr(id, attr, value) click to toggle source
# File lib/uaa/stub/scim.rb, line 291
def set_hidden_attr(id, attr, value)
  raise NotFound unless thing = ref_by_id(id)
  raise ArgumentError unless HIDDEN_ATTRS.include?(attr)
  thing[attr] = value
end
update(id, stuff, match_version = nil, match_type = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 224
def update(id, stuff, match_version = nil, match_type = nil)
  raise NotFound unless thing = ref_by_id(id, match_type)
  raise BadVersion if match_version && match_version != thing[:meta][:version]
  enforce_schema(rtype = thing[:rtype], remove_attrs(stuff, READ_ONLY_ATTRS))
  new_thing = input(stuff)
  if newname = new_thing[NAME_ATTR[rtype]]
    oldname = rtype.to_s + thing[NAME_ATTR[rtype]].downcase
    unless (newname = rtype.to_s + newname.downcase) == oldname
      name = append_origin_to_username(newname, rtype, stuff['origin'])
      raise AlreadyExists if @things_by_name.key?(name)
      @things_by_name.delete(oldname)
      @things_by_name[name] = thing
    end
  end
  if new_thing[:members] || thing[:members]
    old_members = thing[:members] || Set.new
    new_members = new_thing[:members] || Set.new
    delete_user_groups(id, old_members - new_members)
    add_user_groups(id, new_members - old_members)
  end
  READ_ONLY_ATTRS.each { |a| new_thing[a] = thing[a] if thing[a] }
  HIDDEN_ATTRS.each { |a| new_thing[a] = thing[a] if thing[a] }
  thing.replace new_thing
  thing[:meta][:version] += 1
  thing[:meta][:lastmodified] == Time.now.iso8601
  id
end

Private Instance Methods

add_user_groups(gid, members) click to toggle source
# File lib/uaa/stub/scim.rb, line 181
def add_user_groups(gid, members)
  members.each {|m| (m[:groups] ||= Set.new) << gid if m = ref_by_id(m, :user)} if members
end
delete_references(id) click to toggle source
# File lib/uaa/stub/scim.rb, line 189
def delete_references(id)
  @things_by_id.each { |k, v|
    REFERENCES.each { |a| v.delete(a) if v[a] && v[a].delete(id) && v[a].empty? }
  }
end
delete_user_groups(gid, members) click to toggle source
# File lib/uaa/stub/scim.rb, line 185
def delete_user_groups(gid, members)
  members.each {|m| m[:groups].delete(gid) if m = ref_by_id(m, :user) } if members
end
enforce_schema(rtype, stuff) click to toggle source
# File lib/uaa/stub/scim.rb, line 121
def enforce_schema(rtype, stuff)
  stuff.each do |ks, v|
    unless ATTR_NAMES.include?(ks.to_s) && LEGAL_ATTRS[rtype].include?(k = ks.to_sym)
      raise SchemaViolation, "illegal #{ks} on #{rtype}"
    end
    if READ_ONLY_ATTRS.include?(k)
      raise SchemaViolation, "attempt to modify read-only attribute #{k} on #{rtype}"
    end
    valid_attr = case k
      when *BOOLEANS then v == !!v
      when *NUMBERS then v.is_a?(Integer)
      when *GENERAL_MULTI then valid_multi?(v, GENERAL_SUBATTRS, true)
      when *GROUPS then valid_ids?(v, :group)
      when *MEMBERSHIP then valid_ids?(v)
      when ENUMS[k] then ENUMS[k].include?(v)
      # not applicable to client objects (only scimuser objects have complex 'name' or 'meta' attributes)
      when *EXPLICIT_SINGLE.keys && rtype.equal?(:client) then valid_complex?(v, EXPLICIT_SINGLE[k])
      when *EXPLICIT_MULTI.keys then valid_multi?(v, EXPLICIT_MULTI[k])
      else k.is_a?(String) || k.is_a?(Symbol)
    end
    raise SchemaViolation, "#{v} is an invalid #{k}" unless valid_attr
  end
end
input(stuff) click to toggle source
# File lib/uaa/stub/scim.rb, line 145
def input(stuff)
  thing = Util.hash_keys(stuff.dup, :sym)
  REFERENCES.each {|a|
    next unless thing[a]
    thing[a] = thing[a].each_with_object(Set.new) { |r, s|
      s << (r.is_a?(Hash)? r[:value] : r )
    }
  }
  GENERAL_MULTI.each {|a|
    next unless thing[a]
    thing[a] = thing[a].each_with_object({}) { |v, o|
      v = {value: v} unless v.is_a?(Hash)
      # enforce values are unique by type and value
      k = Util.encode_form(t: [v[:type], v: v[:value]]).downcase
      o[k] = v
    }
  }
  thing
end
output(thing, attrs = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 165
def output(thing, attrs = nil)
  attrs = thing.keys if attrs.nil? || attrs.empty?
  attrs.each_with_object({}) {|a, o|
    next if thing[a].nil?
    case a
    when *MEMBERSHIP
      o[a] = thing[a].each_with_object([]) { |v, a|
        a << { value: v, type: ref_by_id(v)[:rtype] }
      }
    when *GROUPS then o[a] = thing[a].to_a
    when *GENERAL_MULTI then o[a] = thing[a].values
    else o[a] = thing[a]
    end
  }
end
ref_by_id(id, rtype = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 98
def ref_by_id(id, rtype = nil)
  (t = @things_by_id[id]) && (rtype.nil? || t[:rtype] == rtype) ? t : nil
end
ref_by_name(name, rtype) click to toggle source
# File lib/uaa/stub/scim.rb, line 96
def ref_by_name(name, rtype) @things_by_name[rtype.to_s + name.downcase] end
remove_attrs(stuff, attrs = HIDDEN_ATTRS) click to toggle source
# File lib/uaa/stub/scim.rb, line 87
def remove_attrs(stuff, attrs = HIDDEN_ATTRS)
  attrs.each { |a| stuff.delete(a.to_s) }
  stuff
end
valid_complex?(value, subattrs, simple_ok = false) click to toggle source
# File lib/uaa/stub/scim.rb, line 102
def valid_complex?(value, subattrs, simple_ok = false)
  return true if simple_ok && value.is_a?(String)
  return unless value.is_a?(Hash) && (!simple_ok || value.key?("value"))
  value.each { |k, v| return unless SUBATTR_NAMES.include?(k) && subattrs.include?(k.to_sym) }
end
valid_id?(id, rtype) click to toggle source
# File lib/uaa/stub/scim.rb, line 92
def valid_id?(id, rtype)
  id && (t = @things_by_id[id]) && (rtype.nil? || t[:rtype] == rtype)
end
valid_ids?(value, rtype = nil) click to toggle source
# File lib/uaa/stub/scim.rb, line 113
def valid_ids?(value, rtype = nil)
  return unless value.is_a?(Array)
  value.each do |ref|
    return unless ref.is_a?(String) && valid_id?(ref, rtype) ||
        ref.is_a?(Hash) && valid_id?(ref["value"], rtype)
  end
end
valid_multi?(values, subattrs, simple_ok = false) click to toggle source
# File lib/uaa/stub/scim.rb, line 108
def valid_multi?(values, subattrs, simple_ok = false)
  return unless values.is_a?(Array)
  values.each { |value| return unless valid_complex?(value, subattrs, simple_ok) }
end