module SSO
  class Apache < Base
    delegate :session, :to => :controller

    CAS_USERNAME = 'REMOTE_USER'
    ENV_TO_ATTR_MAPPING = {
      'REMOTE_USER_EMAIL'     => :mail,
      'REMOTE_USER_FIRSTNAME' => :firstname,
      'REMOTE_USER_LASTNAME'  => :lastname,
    }

    def available?
      return false unless Setting['authorize_login_delegation']
      return false if controller.api_request? and not Setting['authorize_login_delegation_api']
      return false if controller.api_request? and not request.env[CAS_USERNAME].present?
      true
    end

    def support_expiration?
      true
    end

    def support_fallback?
      true
    end

    # If REMOTE_USER is provided by the web server then
    # authenticate the user without using password.
    def authenticated?
      return false unless (self.user = request.env[CAS_USERNAME])
      attrs = { :login => self.user }.merge(additional_attributes)
      group_count = request.env['REMOTE_USER_GROUP_N'].to_i
      if group_count > 0
        attrs[:groups] = []
        group_count.times { |i| attrs[:groups]<< request.env["REMOTE_USER_GROUP_#{i+1}"] }
      end

      return false unless User.find_or_create_external_user(attrs, Setting['authorize_login_delegation_auth_source_user_autocreate'])
      store
      true
    end

    def support_login?
      request.fullpath != self.login_url
    end

    def authenticate!
      self.has_rendered = true
      controller.redirect_to controller.main_app.extlogin_users_path
    end

    def login_url
      controller.main_app.extlogin_users_path
    end

    def logout_url
      Setting['login_delegation_logout_url'] || controller.extlogout_users_path
    end

    def expiration_url
      controller.main_app.extlogin_users_path
    end

    private

    def additional_attributes
      attrs = {}
      ENV_TO_ATTR_MAPPING.each do |header, attribute|
        if request.env.has_key?(header)
          attrs[attribute] = convert_encoding(request.env[header].dup)
        end
      end
      attrs
    end

    def convert_encoding(value)
      if value.respond_to?(:force_encoding)
        value.force_encoding(Encoding::UTF_8)
        if not value.valid_encoding?
          value.encode(Encoding::UTF_8, Encoding::ISO_8859_1, { :invalid => :replace, :replace => '-' }).force_encoding(Encoding::UTF_8)
        end
      else
        Iconv.new('UTF-8//IGNORE', 'UTF-8').iconv(value) rescue value
      end
      value
    end

    def store
      session[:sso_method] = self.class.to_s
    end
  end
end
