RIPAGlobal / scimitar

A SCIM v2 API endpoint implementation
MIT License
61 stars 43 forks source link

Microsoft SCIM filter format incompatibility #115

Closed gsar closed 5 months ago

gsar commented 7 months ago

It looks like Microsoft has documented using emails[type eq "work"].value eq "foo@bar.com". This filter format is also used by Azure's SCIM admin tool to filter by emails by default when executing queries via their UI.

There is a SO thread where a Microsoft employee has acknowledged that this format isn't compliant with the SCIM spec, and that they intend to fix it. However, it is a big blocker for current usage. So I'm wondering if it would be possible to support as a one-off, at least until Microsoft changes their implementation.

A rough cut of the implementation would be to transform emails[type eq "work"].value eq "foo@bar.com" into emails[type eq "work" and value eq "foo@bar.com"] before processing the query. The current code already handles the latter form if scim_queryable_attributes contains mappings for emails.value and emails.type. The python implementation in scim2-filter-parser appears to handle this by transforming it to emails.value[type eq "work"] eq "foo@bar.com" but this form doesn't work in scimitar.

pond commented 5 months ago

Am now looking into this but it's non-trivial since the internal system is processing as a recursive tree walk, having split the path into components. So for example, this legal path:

addresses[type eq "work"].streetAddress

...is processed as two independent, discrete items split on the . (well, not quite - schema IDs have to be handled, but this is the gist of it anyway):

addresses[type eq "work"]
streetAddress

If we take the broken Azure case:

emails[type eq "work"].value eq "foo@bar.com"

...then will be processed as two path components:

emails[type eq "work"]
value eq "foo@bar.com"

...so as you can see, the first one is easily identifiable as a filter - square brackets - while the second one just looks flat out broken, and since we're processing in recursive call as part of a tree walk, the call handling that broken path doesn't "know" that there was a prior filter and it is somehow supposed to recon the whole thing, walk up a level, and munge it together into an and query.

TL;DR, hard problem to solve which might well involve more invasive and extensive changes than we would ever want for a vendor bug workaround, but that's Microsoft to a tee!

EDITED TO NOTE: Given your context of description, can we be happy to never support this for e.g. PATCH operations, and only support this for a GET?

pond commented 5 months ago

@gsar If we're OK with this only being for GET requests, then it can be slotted in quite cleanly. See:

https://github.com/RIPAGlobal/scimitar/pull/128

pond commented 5 months ago

Fix released in https://rubygems.org/gems/scimitar/versions/2.8.0. I'll backport to V1 within a week or two.

pond commented 4 months ago

Now available for Rails 6 via v1.10.0 too.