WinRb / Viewpoint

A Ruby client access library for Microsoft Exchange Web Services (EWS)
Apache License 2.0
248 stars 171 forks source link

Basic authentication is deprecated #287

Open fuzzy76 opened 4 years ago

fuzzy76 commented 4 years ago

Basic authentication is deprecated, and being removed from Exchange Online 13th October. This means that anything using Viewpoint against O365 will fail on that date.

Source: https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/

fliebe92 commented 1 year ago

Hey @fuzzy76, do you have any news about this issue?

Thanks for your collaboration 😄

fuzzy76 commented 1 year ago

I reported this issue as a user, I have no idea what the maintainer(s) is doing about it. 🤷‍♂️ My employer dropped our inhouse exchange integration in favour of one written externally, so it's not my problem anymore.

fliebe92 commented 1 year ago

Thanks for the quick reply @fuzzy76 👍

@zenchild, any news about this issue, or could we consider this repository as stale/unmaintained?

fliebe92 commented 1 year ago

Hey folks 👋 ,

we invested some time to get this working and we would like to share it with the Ruby community, here you go 🚀 :


Usage with OAuth:

Viewpoint::EWSClient.new({
                           endpoint: 'https://outlook.office365.com/EWS/Exchange.asmx',
                           type:     'oauth',
                           token:    'xxx',
                         }, {})

Usage with Basic Auth:

Viewpoint::EWSClient.new({
                           endpoint: 'https://outlook.office365.com/EWS/Exchange.asmx',
                           type:     'basic',
                           user:     'xxx',
                           password: 'xxx',
                         }, {})

Call load_viewpoint_class (where needed) to monkey-patch Viewpoint gem to support OAuth:

def load_viewpoint_class
  return if defined?(Viewpoint::EWS::Connection)

  require 'viewpoint'

  Viewpoint::EWS::Connection.class_eval do
    def initialize(auth, opts = {})
      @log = Logging.logger[self.class.name.to_s.to_sym]

      @httpcli = http_object(opts)

      @auth_type  = auth[:type]
      @auth_token = @auth_type == 'oauth' ? auth[:token] : nil

      @endpoint = auth[:endpoint]
    end

    def post(xmldoc)
      headers = { 'Content-Type' => 'text/xml' }

      if @auth_type == 'oauth' && @auth_token.present?
        headers = headers.merge({ 'Authorization' => "Bearer #{@auth_token}" })
      end

      check_response(@httpcli.post(@endpoint, xmldoc, headers))
    end

    private

    def http_object(opts)
      @httpcli = if opts[:user_agent]
                    HTTPClient.new(agent_name: opts[:user_agent])
                  else
                    HTTPClient.new
                  end

      trust_ca_option(opts)
      ssl_config(opts)
      timeout_options(opts)

      @httpcli
    end

    def trust_ca_option(opts)
      return if opts[:trust_ca].nil?

      @httpcli.ssl_config.clear_cert_store
      opts[:trust_ca].each do |ca|
        @httpcli.ssl_config.add_trust_ca ca
      end
    end

    def ssl_config(opts)
      @httpcli.ssl_config.verify_mode = opts[:ssl_verify_mode] if opts[:ssl_verify_mode]
      @httpcli.ssl_config.ssl_version = opts[:ssl_version] if opts[:ssl_version]
    end

    def timeout_options(opts)
      # Up the keep-alive so we don't have to do the NTLM dance as often.
      @httpcli.keep_alive_timeout = 60
      @httpcli.receive_timeout = opts[:receive_timeout] if opts[:receive_timeout]
      @httpcli.connect_timeout = opts[:connect_timeout] if opts[:connect_timeout]
    end
  end

  Viewpoint::EWSClient.class_eval do
    def initialize(auth, opts = {})
      auth = auth.dup

      @auth_type  = auth[:type]
      @auth_token = @auth_type == 'oauth' ? auth[:token] : nil

      @endpoint   = auth[:endpoint]
      @username   = auth[:user]
      password    = @auth_type == 'basic' ? auth[:password] : nil

      http_klass = opts[:http_class] || Viewpoint::EWS::Connection
      connection = http_klass.new(auth, opts[:http_opts] || {})
      connection.set_auth(@username, password) if password.present?

      @ews = Viewpoint::EWS::SOAP::ExchangeWebService.new(connection, opts)
    end
  end
end
pcai commented 2 months ago

Hi - I recently took over maintenance for a few gems in this org. @fliebe92 I tried to integrate your suggested fix into https://github.com/WinRb/Viewpoint/compare/main...oauth - do I have your permission to merge your contribution into main and make a release?

fliebe92 commented 2 months ago

Hi - I recently took over maintenance for a few gems in this org. @fliebe92 I tried to integrate your suggested fix into main...oauth - do I have your permission to merge your contribution into main and make a release?

Hi @pcai. Feel free to merge it and make a release. Thanks! :)