<commit_before>require 'builder'
require 'faraday'
require 'multi_xml'

module OmniAuth
  module Strategies
    # Authenticate to Microsoft HealthVault service utilizing OAuth 2.0 and retrieve basic user information.
    # Documentation available here:
    # http://msdn.microsoft.com/library/jj863179
    #
    # @example Basic Usage
    #   use OmniAuth::Strategies::HealthVault, 'app_id', [:settings]
    #
    class Healthvault
      include OmniAuth::Strategy

      PLATFORM_VERSION = '1.12.1002.8529'

      args [:app_id, :pubkey_thumbprint, :cert_file]

      option :app_id, nil

      option :platform_url
      option :shell_url, 'https://account.healthvault.com/redirect.aspx'
      option :platform_url, 'https://platform.healthvault.com/platform/wildcat.ashx'

      def request_phase
        url = "#{options[:shell_url]}?target=AUTH&targetqs=appid%3D#{options[:app_id]}"
        redirect url
      end

      def callback_phase
        if request.params['target'] == 'AppAuthSuccess'
          shared_secret = Base64.encode64(SecureRandom.hex)
          app_auth_token = create_authenticated_session_token(shared_secret)
          get_person_info
        end
        super
      end

      private

      def create_authenticated_session_token(shared_secret)
        body = build_create_authenticated_session_token_request(shared_secret)
        conn = ::Faraday.new(url: options[:platform_url]) do |faraday|
          faraday.request  :url_encoded             # form-encode POST params
          faraday.response :logger                  # log requests to STDOUT
          faraday.adapter  ::Faraday.default_adapter  # make requests with Net::HTTP
        end
        response = conn.post do |i|
          i.headers['Content-Type'] = 'text/xml'
          i.body = body
        end
        parse_create_authenticated_session_token_response(response.body)
      end

      def get_person_info

      end

      def build_create_authenticated_session_token_request(shared_secret)
        content = ::Builder::XmlMarkup.new
        content = content.content do
          content.tag! 'app-id', options[:app_id]
          content.tag! 'shared-secret' do
            content.tag! 'hmac-alg', shared_secret, 'algName' => 'HMACSHA1'
          end
        end
        pem = File.read(options[:cert_file])
        signature = Base64.encode64(OpenSSL::PKey::RSA.new(pem).sign(OpenSSL::Digest::SHA1.new, content))
        body = ::Builder::XmlMarkup.new
        body.tag!('wc-request:request', 'xmlns:wc-request' => 'urn:com.microsoft.wc.request') do
          body.header do
            body.method 'CreateAuthenticatedSessionToken'
            body.tag! 'method-version', 1
            body.tag! 'app-id', options[:app_id]
            body.language 'en'
            body.country 'US'
            body.tag! 'msg-time', Time.now.to_datetime.rfc3339
            body.tag! 'msg-ttl', 36000
            body.version PLATFORM_VERSION
          end
          body.info do
            body.tag! 'auth-info' do
              body.tag! 'app-id', options[:app_id]
              body.credential do
                body.appserver do
                  body.sig signature, 'digestMethod' => 'SHA1', 'sigMethod' => 'RSA-SHA1', 'thumbprint' => options[:pubkey_thumbprint]
                  body << content
                end
              end
            end
          end
        end
      end

      def parse_create_authenticated_session_token_response(response_body)
        result = ::MultiXml.parse(response_body)
        result['response']['info']['token']['__content__'] rescue nil
      end
    end
  end
end<commit_msg>GetPersonInfo implemented!<commit_after>require 'builder'
require 'faraday'
require 'multi_xml'

module OmniAuth
  module Strategies
    # Authenticate to Microsoft HealthVault service utilizing OAuth 2.0 and retrieve basic user information.
    # Documentation available here:
    # http://msdn.microsoft.com/library/jj863179
    #
    # @example Basic Usage
    #   use OmniAuth::Strategies::HealthVault, 'app_id', [:settings]
    #
    class Healthvault
      include OmniAuth::Strategy

      PLATFORM_VERSION = '1.12.1002.8529'

      args [:app_id, :pubkey_thumbprint, :cert_file]

      option :app_id, nil

      option :platform_url
      option :shell_url, 'https://account.healthvault.com/redirect.aspx'
      option :platform_url, 'https://platform.healthvault.com/platform/wildcat.ashx'

      def request_phase
        url = "#{options[:shell_url]}?target=AUTH&targetqs=appid%3D#{options[:app_id]}"
        redirect url
      end

      def callback_phase
        if request.params['target'] == 'AppAuthSuccess'
          @certificate = File.read(options[:cert_file])
          @wctoken = request.params['wctoken']
          @shared_secret = Base64.encode64(SecureRandom.hex)
          @app_auth_token = create_authenticated_session_token
          Rails.logger.info("PERSON INFO: #{get_person_info}")
        end
        super
      end

      private

      def send_request(body)
        conn = ::Faraday.new(url: options[:platform_url]) do |faraday|
          faraday.request  :url_encoded             # form-encode POST params
          faraday.response :logger                  # log requests to STDOUT
          faraday.adapter  ::Faraday.default_adapter  # make requests with Net::HTTP
        end
        conn.post do |i|
          i.headers['Content-Type'] = 'text/xml'
          i.body = body
        end
      end

      def create_authenticated_session_token
        response = send_request(build_create_authenticated_session_token_request)
        parse_create_authenticated_session_token_response(response.body)
      end

      def get_person_info
        body = build_get_person_info_request
        Rails.logger.info "BODY: #{body}"
        response = send_request(body)
        parse_get_person_info_response(response.body)
      end

      def build_get_person_info_request
        info = ::Builder::XmlMarkup.new.info
        header = ::Builder::XmlMarkup.new.header do |header|
          header.method 'GetPersonInfo'
          header.tag! 'method-version', 1
          header.tag! 'auth-session' do
            header.tag! 'auth-token', @app_auth_token
            header.tag! 'user-auth-token', @<PASSWORD>
          end
          header.language 'en'
          header.country 'US'
          header.tag! 'msg-time', Time.now.to_datetime.rfc3339
          header.tag! 'msg-ttl', 36000
          header.version PLATFORM_VERSION
          header.tag! 'info-hash' do
            header.tag! 'hash-data', Base64.encode64(OpenSSL::Digest::SHA1.digest(info)).strip, 'algName' => 'SHA1'
          end
        end
        body = ::Builder::XmlMarkup.new
        body.tag!('wc-request:request', 'xmlns:wc-request' => 'urn:com.microsoft.wc.request') do
          body.auth do
            body.tag! 'hmac-data', Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, Base64.decode64(@shared_secret), header)).strip, 'algName' => 'HMACSHA1'
          end
          body << header
          body << info
        end
      end

      def parse_get_person_info_response(response_body)
        result = ::MultiXml.parse(response_body)
        result['response'] rescue nil
      end

      def build_create_authenticated_session_token_request
        content = ::Builder::XmlMarkup.new
        content = content.content do
          content.tag! 'app-id', options[:app_id]
          content.tag! 'shared-secret' do
            content.tag! 'hmac-alg', @shared_secret, 'algName' => 'HMACSHA1'
          end
        end
        signature = Base64.encode64(OpenSSL::PKey::RSA.new(@certificate).sign(OpenSSL::Digest::SHA1.new, content))
        body = ::Builder::XmlMarkup.new
        body.tag!('wc-request:request', 'xmlns:wc-request' => 'urn:com.microsoft.wc.request') do
          body.header do
            body.method 'CreateAuthenticatedSessionToken'
            body.tag! 'method-version', 1
            body.tag! 'app-id', options[:app_id]
            body.language 'en'
            body.country 'US'
            body.tag! 'msg-time', Time.now.to_datetime.rfc3339
            body.tag! 'msg-ttl', 36000
            body.version PLATFORM_VERSION
          end
          body.info do
            body.tag! 'auth-info' do
              body.tag! 'app-id', options[:app_id]
              body.credential do
                body.appserver do
                  body.sig signature, 'digestMethod' => 'SHA1', 'sigMethod' => 'RSA-SHA1', 'thumbprint' => options[:pubkey_thumbprint]
                  body << content
                end
              end
            end
          end
        end
      end

      def parse_create_authenticated_session_token_response(response_body)
        result = ::MultiXml.parse(response_body)
        result['response']['info']['token']['__content__'] rescue nil
      end
    end
  end
end
{'fraction_non_alphanumeric': 0.10571396371013186, 'fraction_numerical': 0.01239715992336301, 'mean_word_length': 2.305027932960894, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 17, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 7}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3417617', 'n_tokens_mistral': 2734, 'n_tokens_neox': 2591, 'n_words': 509}