Commit 26e5f92
Changed files (7)
app
controllers
scim
spec
models
requests
scim
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)