Class: TwoPercent::ScimUser

Inherits:
ApplicationRecord show all
Defined in:
app/models/two_percent/scim_user.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.destroy_by_scim_id(scim_id) ⇒ Object



47
48
49
# File 'app/models/two_percent/scim_user.rb', line 47

def self.destroy_by_scim_id(scim_id)
  find_by_scim_id(scim_id)&.destroy
end

.exists_by_scim_id?(scim_id) ⇒ Boolean

Returns:

  • (Boolean)


43
44
45
# File 'app/models/two_percent/scim_user.rb', line 43

def self.exists_by_scim_id?(scim_id)
  exists?(scim_id: scim_id)
end

.find_by_scim_id(scim_id) ⇒ Object



39
40
41
# File 'app/models/two_percent/scim_user.rb', line 39

def self.find_by_scim_id(scim_id)
  find_by(scim_id: scim_id)
end

.upsert_from_scim(scim_hash, correlation_id: nil) ⇒ TwoPercent::ScimUser

Creates or updates a user from SCIM data

Generates a UUID for the id field if not present (for POST/create operations). Validates the SCIM data against the User schema before persisting.

Parameters:

  • scim_hash (Hash)

    SCIM User resource hash conforming to RFC 7643

  • correlation_id (String, nil) (defaults to: nil)

    Optional correlation ID for tracking changes across network hops (e.g., App A -> App B -> App C)

Returns:

Raises:

  • (TwoPercent::Scim::ValidationError)

    If SCIM data fails schema validation



28
29
30
31
32
33
34
35
36
37
# File 'app/models/two_percent/scim_user.rb', line 28

def self.upsert_from_scim(scim_hash, correlation_id: nil)
  # Generate ID if not present (for POST/create operations)
  scim_hash = scim_hash.dup
  scim_hash["id"] ||= SecureRandom.uuid

  validated_data = TwoPercent::Scim::Schema.validate_user(scim_hash, require_id: true)
  scim_user = find_or_initialize_by(scim_id: scim_hash["id"])
  scim_user.update_from_scim!(validated_data, correlation_id: correlation_id)
  scim_user
end

Instance Method Details

#extension_attributes(schema_urn = nil) ⇒ Object



118
119
120
121
122
123
124
# File 'app/models/two_percent/scim_user.rb', line 118

def extension_attributes(schema_urn = nil)
  if schema_urn
    scim_data[schema_urn] || {}
  else
    scim_data.select { |k, _| k.start_with?("urn:ietf:params:scim:schemas:extension:") }
  end
end

#scim_attribute(path) ⇒ Object?

Extracts a nested attribute from the scim_data JSON

Examples:

user.scim_attribute("emails.0.value") # => "user@example.com"

Parameters:

  • path (String)

    Dot-separated path to the attribute (e.g., “name.givenName”)

Returns:

  • (Object, nil)

    The attribute value or nil if not found



113
114
115
116
# File 'app/models/two_percent/scim_user.rb', line 113

def scim_attribute(path)
  keys = path.split(".")
  scim_data.dig(*keys)
end

#sync_groups(groups_data) ⇒ Object



99
100
101
102
103
104
105
# File 'app/models/two_percent/scim_user.rb', line 99

def sync_groups(groups_data)
  return if groups_data.blank?

  group_ids = groups_data.filter_map { |g| g["value"] }
  groups = TwoPercent::ScimGroup.where(scim_id: group_ids)
  self.scim_groups = groups
end

#to_domain_attributesHash

Extracts domain attributes for publishing in domain events

Returns key attributes for event payloads. Includes associated group memberships if loaded.

Returns:

  • (Hash)

    Domain attributes



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/models/two_percent/scim_user.rb', line 57

def to_domain_attributes
  attributes = {
    scim_id: scim_id,
    external_id: external_id,
    user_name: user_name,
    display_name: display_name,
    email: email,
    active: active,
  }

  attributes[:groups] = group_memberships_attributes if scim_groups.loaded? || scim_groups.any?
  attributes.compact
end

#to_scim_representationHash

Returns full SCIM representation for HTTP responses

Returns:

  • (Hash)

    RFC 7644 compliant SCIM User resource



74
75
76
77
78
79
80
81
82
83
# File 'app/models/two_percent/scim_user.rb', line 74

def to_scim_representation
  scim_data.merge(
    "id" => scim_id,
    "meta" => {
      "resourceType" => "User",
      "created" => created_at.iso8601,
      "lastModified" => updated_at.iso8601,
    }
  )
end

#update_from_scim!(validated_data, correlation_id: nil) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/models/two_percent/scim_user.rb', line 85

def update_from_scim!(validated_data, correlation_id: nil)
  core_data = validated_data[:core]
  self.scim_data = core_data.merge(validated_data[:extensions])
  self.scim_id = core_data["id"]
  self.external_id = core_data["externalId"]
  self.user_name = core_data["userName"]
  self.display_name = core_data["displayName"]
  self.email = core_data.dig("emails", 0, "value")
  self.active = core_data.fetch("active", true)
  self.correlation_id = correlation_id
  save!
  sync_groups(core_data["groups"]) if core_data["groups"]
end