Commit 26e5f92

mo <mo@mokhan.ca>
2019-05-14 20:34:52
parse a very simple scim query filter
1 parent bbcaf5d
Changed files (7)
app/controllers/scim/v2/users_controller.rb
@@ -10,7 +10,11 @@ module Scim
       end
 
       def index
-        @users = paginate(User.order(:created_at), page: page - 1, page_size: page_size)
+        if params[:filter].present?
+          @users = paginate(apply_filter_to(User.order(:created_at), params[:filter]), page: page - 1, page_size: page_size)
+        else
+          @users = paginate(User.order(:created_at), page: page - 1, page_size: page_size)
+        end
 
         render formats: :scim, status: :ok
       end
@@ -55,6 +59,12 @@ module Scim
       def page_size
         page_param(:count, default: 25, bottom: 0, top: 25)
       end
+
+      def apply_filter_to(scope, raw_filter)
+        parser = Scim::Kit::V2::Filter.new
+        parse_tree = parser.parse(params[:filter])
+        scope.scim_filter_for(parse_tree)
+      end
     end
   end
 end
app/models/scim/user.rb
@@ -2,6 +2,9 @@
 
 module SCIM
   class User
+    ATTRIBUTES = {
+      userName: :email,
+    }.with_indifferent_access
     include ActiveModel::Model
     attr_accessor :id, :schemas, :userName, :name, :locale, :timezone, :password
 
app/models/user.rb
@@ -13,6 +13,18 @@ class User < ApplicationRecord
   validates :timezone, inclusion: VALID_TIMEZONES
   validates :locale, inclusion: VALID_LOCALES
 
+  scope :scim_filter_for, -> (tree) do
+    attr = SCIM::User::ATTRIBUTES[tree[:attribute].to_s] || tree[:attribute].to_s
+    case tree[:comparison_operator].to_s
+    when 'eq'
+      where(attr => tree[:comparison_value].to_s[1..-2])
+    when 'ne'
+      where.not(attr => tree[:comparison_value].to_s[1..-2])
+    else
+      self
+    end
+  end
+
   def name_id_for(name_id_format)
     Saml::Kit::Namespaces::PERSISTENT == name_id_format ? id : email
   end
spec/models/user_spec.rb
@@ -10,4 +10,25 @@ RSpec.describe User do
 
     specify { expect(subject.sessions).to match_array([user_session]) }
   end
+
+  describe ".scim_filter_for" do
+    let!(:users) { create_list(:user, 10) }
+    let(:random_user) { users.sample  }
+
+    specify do
+      expect(User.scim_filter_for(
+        attribute: "userName",
+        comparison_operator: "eq",
+        comparison_value: "\"#{random_user.email}\""
+      )).to match_array([random_user])
+    end
+
+    specify do
+      expect(User.scim_filter_for(
+        attribute: "userName",
+        comparison_operator: "ne",
+        comparison_value: "\"#{random_user.email}\""
+      ).pluck(:email)).not_to include(random_user.email)
+    end
+  end
 end
spec/requests/scim/v2/users_spec.rb
@@ -207,7 +207,7 @@ describe '/scim/v2/users' do
       context "when searching for an exact match on one field" do
         let(:matching_user) { users.sample }
 
-        before { get "/scim/v2/users", params: { filter: "userName eq #{matching_user.email}" }, headers: headers }
+        before { get "/scim/v2/users", params: { filter: "userName eq \"#{matching_user.email}\"" }, headers: headers }
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(json[:totalResults]).to be(1) }
Gemfile
@@ -22,7 +22,7 @@ gem 'puma', '~> 3.11'
 gem 'rails', '~> 5.2.0'
 gem 'rotp', '~> 3.3'
 gem 'saml-kit', '~> 1.0'
-gem 'scim-kit', '~> 0.2'
+gem 'scim-kit', github: 'mokhan/scim-kit'
 gem 'spank', '~> 1.0'
 gem 'turbolinks', '~> 5'
 gem 'webpacker', '~> 3.5'
Gemfile.lock
@@ -1,3 +1,14 @@
+GIT
+  remote: https://github.com/mokhan/scim-kit.git
+  revision: fef65ad03e7268516c418b5eefd7d45add5d25f0
+  specs:
+    scim-kit (0.3.2)
+      activemodel (>= 5.2.0)
+      net-hippie (~> 0.2)
+      parslet (~> 1.8)
+      tilt (~> 2.0)
+      tilt-jbuilder (~> 0.7)
+
 GEM
   remote: https://rubygems.org/
   specs:
@@ -66,14 +77,14 @@ GEM
     bindex (0.7.0)
     bootsnap (1.4.4)
       msgpack (~> 1.0)
-    brakeman (4.5.0)
+    brakeman (4.5.1)
     browser (2.5.3)
     builder (3.2.3)
     bundler-audit (0.6.1)
       bundler (>= 1.2.0, < 3)
       thor (~> 0.18)
     byebug (11.0.1)
-    capybara (3.18.0)
+    capybara (3.19.1)
       addressable
       mini_mime (>= 0.1.3)
       nokogiri (~> 1.8)
@@ -139,9 +150,8 @@ GEM
       rainbow (>= 2.2.2, < 4.0)
       terminal-table (>= 1.5.1)
     jaro_winkler (1.5.2)
-    jbuilder (2.8.0)
+    jbuilder (2.9.1)
       activesupport (>= 4.2.0)
-      multi_json (>= 1.2)
     jekyll (3.8.5)
       addressable (~> 2.4)
       colorator (~> 1.0)
@@ -199,6 +209,7 @@ GEM
     parallel (1.17.0)
     parser (2.6.3.0)
       ast (~> 2.4.0)
+    parslet (1.8.2)
     pathutil (0.16.2)
       forwardable-extended (~> 2.6)
     pg (1.1.4)
@@ -266,14 +277,14 @@ GEM
       rspec-mocks (~> 3.8.0)
       rspec-support (~> 3.8.0)
     rspec-support (3.8.0)
-    rubocop (0.68.1)
+    rubocop (0.69.0)
       jaro_winkler (~> 1.5.1)
       parallel (~> 1.10)
-      parser (>= 2.5, != 2.5.1.1)
+      parser (>= 2.6)
       rainbow (>= 2.2.2, < 4.0)
       ruby-progressbar (~> 1.7)
-      unicode-display_width (>= 1.4.0, < 1.6)
-    rubocop-rspec (1.32.0)
+      unicode-display_width (>= 1.4.0, < 1.7)
+    rubocop-rspec (1.33.0)
       rubocop (>= 0.60.0)
     ruby-progressbar (1.10.0)
     ruby_dep (1.5.0)
@@ -288,15 +299,10 @@ GEM
     sass-listen (4.0.0)
       rb-fsevent (~> 0.9, >= 0.9.4)
       rb-inotify (~> 0.9, >= 0.9.7)
-    scim-kit (0.3.2)
-      activemodel (>= 5.2.0)
-      net-hippie (~> 0.2)
-      tilt (~> 2.0)
-      tilt-jbuilder (~> 0.7)
-    selenium-webdriver (3.142.1)
+    selenium-webdriver (3.142.2)
       childprocess (>= 0.5, < 2.0)
       rubyzip (~> 1.2, >= 1.2.2)
-    smart_properties (1.13.1)
+    smart_properties (1.14.0)
     spank (1.0.1441140881)
     sprockets (3.7.2)
       concurrent-ruby (~> 1.0)
@@ -318,7 +324,7 @@ GEM
     turbolinks-source (5.2.0)
     tzinfo (1.2.5)
       thread_safe (~> 0.1)
-    unicode-display_width (1.5.0)
+    unicode-display_width (1.6.0)
     vcr (4.0.0)
     web-console (3.7.0)
       actionview (>= 5.0)
@@ -385,7 +391,7 @@ DEPENDENCIES
   rubocop (~> 0.59)
   rubocop-rspec (~> 1.30)
   saml-kit (~> 1.0)
-  scim-kit (~> 0.2)
+  scim-kit!
   selenium-webdriver (~> 3.14)
   spank (~> 1.0)
   turbolinks (~> 5)