main
 1# frozen_string_literal: true
 2
 3module Scim
 4  class Controller < ActionController::API
 5    include ActionController::HttpAuthentication::Token::ControllerMethods
 6    before_action :apply_scim_content_type
 7    before_action :ensure_correct_content_type!
 8    before_action :authenticate!
 9    helper_method :current_user, :scim_type_for
10    rescue_from StandardError do |error|
11      Rails.logger.error(error)
12      render "server_error", status: :server_error
13    end
14    rescue_from ActiveRecord::RecordInvalid, with: :record_invalid
15    rescue_from ActiveModel::ValidationError, with: :record_invalid
16    rescue_from ActiveRecord::RecordNotFound, with: :not_found
17
18    def current_user
19      Current.user
20    end
21
22    def current_user?
23      Current.user?
24    end
25
26    protected
27
28    def not_found
29      render json: {
30        schemas: [Scim::Kit::V2::Messages::ERROR],
31        detail: "Resource #{params[:id]} not found",
32        status: "404",
33      }.to_json, status: :not_found
34    end
35
36    def record_invalid(error)
37      @error = error
38      @model = error.respond_to?(:model) ? error.model : error.record
39      render "record_invalid", status: :bad_request
40    end
41
42    private
43
44    def authenticate!
45      Current.token = authenticate_with_http_token do |token|
46        Token.authenticate(token)
47      end
48      options = { status: :unauthorized, formats: :scim }
49      render "unauthorized", options unless Current.user?
50    end
51
52    def apply_scim_content_type
53      response.headers['Content-Type'] = Mime[:scim].to_s
54    end
55
56    def ensure_correct_content_type!
57      return if acceptable_content_type?
58
59      status = :unsupported_media_type
60      render 'unsupported_media_type', status: status, formats: :scim
61    end
62
63    def acceptable_content_type?
64      [:scim, :json].include?(request&.content_mime_type&.symbol)
65    end
66
67    def scim_type_for(error)
68      case error
69      when ActiveRecord::RecordInvalid
70        errors = error.record.errors.full_messages
71        if errors.count == 1 &&
72           errors[0].end_with?('has already been taken')
73          return 'uniqueness'
74        end
75      end
76      "invalidValue"
77    end
78  end
79end