Skip to content

Latest commit

 

History

History
102 lines (76 loc) · 3.28 KB

File metadata and controls

102 lines (76 loc) · 3.28 KB

Filter syntax

SCIM clients can filter list responses (GET /Users?filter=…) and search endpoints (POST /Users/.search) using the SCIM 2.0 filter grammar defined in RFC 7644 §3.4.2.2. AshScim parses these filters into the map-shaped input format consumed by Ash.Query.filter_input/2, with the security-critical attribute resolution done in the same pass.

Operator support

SCIM Ash Example
eq eq userName eq "alice"
ne not_eq active ne true
co contains userName co "alice"
sw string_starts_with? userName sw "alice"
ew string_ends_with? userName ew "@example.com"
pr is_nil: false externalId pr
gt greater_than created gt "2024-01-01"
ge greater_than_or_equal created ge "2024-01-01"
lt less_than created lt "2024-12-31"
le less_than_or_equal created le "2024-12-31"

Boolean composition

and, or, and parenthesized not (...) are supported with the standard precedence (not > and > or):

userName eq "alice" or userName eq "bob"
active eq true and externalId pr
not (active eq true)
(userName co "alice" or userName co "bob") and active eq true

Chained ands and ors flatten into a single list:

  • a or b or c parses to %{or: [a, b, c]}
  • a and b and c parses to %{and: [a, b, c]}

Dotted paths

Sub-attributes of complex and multivalued mappings are addressable via dotted paths:

name.familyName co "Anderson"
emails.value eq "alice@example.com"

For complex and single-attribute multivalueds, the dotted path resolves through the sub-maps to the underlying scalar attribute and emits a flat filter (%{last_name: %{contains: "Anderson"}}).

For relationship-backed multivalueds, the dotted path emits Ash's native nested filter form:

# members.value eq "u1" against a Group
%{memberships: %{user_id: %{eq: "u1"}}}

This traverses the :memberships relationship, so a query like ?filter=members.value eq "u1" finds groups whose memberships include the matching user.

Security: closed-world resolution

The filter resolver is the security boundary between IdP-supplied input and your data layer. The rule it enforces:

Every attribute path in the filter must be declared in the resource's SCIM mappings. Anything else fails closed.

If an IdP sends ?filter=password eq "hunter2", the resolver sees that password is not a declared SCIM attribute on this resource and returns 400 invalidFilter — even if the resource has a password Ash attribute. The filter cannot reach the data layer.

This means filters can only target what the DSL exposes. If you have attribute :department, :string on the resource but don't write map :department, attribute: :department, IdP filters can't reference it. For SCIM that's correct — you don't expose what you didn't declare.

Sorting

SCIM ?sortBy= parameters go through the same resolution pipeline. A sort by an unmapped attribute fails with 400 invalidValue.

GET /Users?sortBy=userName&sortOrder=descending

sortOrder accepts ascending (default) or descending.