Commit 0dd98b5
Changed files (4)
app
controllers
scim
helpers
views
spec
requests
scim
app/controllers/scim/controller.rb
@@ -4,9 +4,15 @@ module Scim
class Controller < ActionController::Base
protect_from_forgery with: :null_session
before_action :apply_scim_content_type
+ before_action :ensure_correct_content_type!
before_action :authenticate!
helper_method :current_user
rescue_from ActiveRecord::RecordNotFound, with: :not_found
+ rescue_from ActiveRecord::RecordInvalid do |error|
+ @error = error
+ @model = error.record
+ render "record_invalid", status: :bad_request
+ end
def current_user
@current_user ||= authenticate_with_http_token do |token|
@@ -37,5 +43,13 @@ module Scim
def apply_scim_content_type
response.headers['Content-Type'] = Mime[:scim].to_s
end
+
+ def ensure_correct_content_type!
+ render 'unsupported_media_type', status: :unsupported_media_type, formats: :scim unless acceptable_content_type?
+ end
+
+ def acceptable_content_type?
+ [:scim, :json].include?(request&.content_mime_type&.symbol)
+ end
end
end
app/helpers/application_helper.rb
@@ -26,4 +26,13 @@ module ApplicationHelper
'🤷'
end
end
+
+ def scim_type_for(error)
+ case error
+ when ActiveRecord::RecordInvalid
+ errors = error.record.errors.full_messages
+ return 'uniqueness' if errors.count == 1 && errors[0].end_with?('has already been taken')
+ end
+ "invalidValue"
+ end
end
app/views/scim/record_invalid.scim.jbuilder
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+json.schemas ["urn:ietf:params:scim:api:messages:2.0:Error"]
+json.scimType scim_type_for(@error)
+json.detail @model.errors.full_messages.join('. ')
+json.status "400"
spec/requests/scim/v2/users_spec.rb
@@ -12,27 +12,44 @@ describe '/scim/v2/users' do
end
describe "POST /scim/v2/users" do
- let(:email) { FFaker::Internet.email }
+ context "when a valid request is sent" do
+ let(:email) { FFaker::Internet.email }
+
+ it 'creates a new user' do
+ body = { schemas: [Scim::Shady::Schemas::USER], userName: email }
+
+ post '/scim/v2/users', params: body.to_json, headers: headers
+
+ expect(response).to have_http_status(:created)
+ expect(response.headers['Content-Type']).to eql('application/scim+json')
+ expect(response.headers['Location']).to be_present
+ expect(response.body).to be_present
+
+ json = JSON.parse(response.body, symbolize_names: true)
+ expect(json[:schemas]).to match_array([Scim::Shady::Schemas::USER])
+ expect(json[:id]).to be_present
+ expect(json[:userName]).to eql(email)
+ expect(json[:meta][:resourceType]).to eql('User')
+ expect(json[:meta][:created]).to be_present
+ expect(json[:meta][:lastModified]).to be_present
+ expect(json[:meta][:version]).to be_present
+ expect(json[:meta][:location]).to be_present
+ end
+ end
- it 'creates a new user' do
- body = { schemas: [Scim::Shady::Schemas::USER], userName: email }
+ context "when a duplicate email is specified" do
+ let(:other_user) { create(:user) }
+ let(:request_body) do
+ { schemas: [Scim::Shady::Schemas::USER], userName: other_user.email }
+ end
- post '/scim/v2/users', params: body.to_json, headers: headers
+ before { post '/scim/v2/users', params: request_body.to_json, headers: headers }
- expect(response).to have_http_status(:created)
- expect(response.headers['Content-Type']).to eql('application/scim+json')
- expect(response.headers['Location']).to be_present
- expect(response.body).to be_present
-
- json = JSON.parse(response.body, symbolize_names: true)
- expect(json[:schemas]).to match_array([Scim::Shady::Schemas::USER])
- expect(json[:id]).to be_present
- expect(json[:userName]).to eql(email)
- expect(json[:meta][:resourceType]).to eql('User')
- expect(json[:meta][:created]).to be_present
- expect(json[:meta][:lastModified]).to be_present
- expect(json[:meta][:version]).to be_present
- expect(json[:meta][:location]).to be_present
+ specify { expect(response).to have_http_status(:bad_request) }
+ specify { expect(JSON.parse(response.body, symbolize_names: true)[:schemas]).to match_array(['urn:ietf:params:scim:api:messages:2.0:Error']) }
+ specify { expect(JSON.parse(response.body, symbolize_names: true)[:scimType]).to eql('uniqueness') }
+ specify { expect(JSON.parse(response.body, symbolize_names: true)[:detail]).to be_instance_of(String) }
+ specify { expect(JSON.parse(response.body, symbolize_names: true)[:status]).to eql('400') }
end
end