File: auth.rb

package info (click to toggle)
ruby-httpx 1.7.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,816 kB
  • sloc: ruby: 12,209; makefile: 4
file content (153 lines) | stat: -rw-r--r-- 4,314 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# frozen_string_literal: true

module HTTPX
  module Plugins
    #
    # This plugin adds a shim +authorization+ method to the session, which will fill
    # the HTTP Authorization header, and another, +bearer_auth+, which fill the "Bearer " prefix
    # in its value.
    #
    # https://gitlab.com/os85/httpx/wikis/Auth#auth
    #
    module Auth
      def self.subplugins
        {
          retries: AuthRetries,
        }
      end

      # adds support for the following options:
      #
      # :auth_header_value :: the token to use as a string, or a callable which returns a string when called.
      # :auth_header_type :: the authentication type to use in the "authorization" header value (i.e. "Bearer", "Digest"...)
      # :generate_auth_value_on_retry :: callable which returns whether the request should regenerate the auth_header_value
      #                                  when the request is retried (this option will only work if the session also loads the
      #                                  <tt>:retries</tt> plugin).
      module OptionsMethods
        def option_auth_header_value(value)
          value
        end

        def option_auth_header_type(value)
          value
        end

        def option_generate_auth_value_on_retry(value)
          raise TypeError, "`:generate_auth_value_on_retry` must be a callable" unless value.respond_to?(:call)

          value
        end
      end

      module InstanceMethods
        def initialize(*)
          super

          @auth_header_value = nil
          @skip_auth_header_value = false
        end

        def authorization(token = nil, auth_header_type: nil, &blk)
          with(auth_header_type: auth_header_type, auth_header_value: token || blk)
        end

        def bearer_auth(token = nil, &blk)
          authorization(token, auth_header_type: "Bearer", &blk)
        end

        def skip_auth_header
          @skip_auth_header_value = true
          yield
        ensure
          @skip_auth_header_value = false
        end

        def reset_auth_header_value!
          @auth_header_value = nil
        end

        private

        def send_request(request, *)
          return super if @skip_auth_header_value || request.authorized?

          @auth_header_value ||= generate_auth_token

          request.authorize(@auth_header_value) if @auth_header_value

          super
        end

        def generate_auth_token
          return unless (auth_value = @options.auth_header_value)

          auth_value = auth_value.call(self) if dynamic_auth_token?(auth_value)

          auth_value
        end

        def dynamic_auth_token?(auth_header_value)
          auth_header_value&.respond_to?(:call)
        end
      end

      module RequestMethods
        def initialize(*)
          super
          @auth_token_value = nil
        end

        def authorized?
          !@auth_token_value.nil?
        end

        def unauthorize!
          return unless (auth_value = @auth_token_value)

          @headers.get("authorization").delete(auth_value)

          @auth_token_value = nil
        end

        def authorize(auth_value)
          if (auth_type = @options.auth_header_type)
            auth_value = "#{auth_type} #{auth_value}"
          end

          @headers.add("authorization", auth_value)

          @auth_token_value = auth_value
        end
      end

      module AuthRetries
        module InstanceMethods
          private

          def retryable_request?(request, response, options)
            super || auth_error?(response, options)
          end

          def retryable_response?(response, options)
            auth_error?(response, options) || super
          end

          def prepare_to_retry(request, response)
            super

            return unless auth_error?(response, request.options) ||
                          (@options.generate_auth_value_on_retry && @options.generate_auth_value_on_retry.call(response))

            request.unauthorize!
            @auth_header_value = generate_auth_token
          end

          def auth_error?(response, options)
            response.is_a?(Response) && response.status == 401 && dynamic_auth_token?(options.auth_header_value)
          end
        end
      end
    end
    register_plugin :auth, Auth
  end
end