Commit 0dd98b5

mo <mo.khan@gmail.com>
2018-09-04 23:18:17
render error when duplicate email is specified.
1 parent 8404f91
Changed files (4)
app
spec
requests
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