File: basic.rb

package info (click to toggle)
ruby-httpauth 0.2.1%2Bgh-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 192 kB
  • sloc: ruby: 925; makefile: 2
file content (113 lines) | stat: -rw-r--r-- 4,920 bytes parent folder | download | duplicates (3)
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
%w(base64 httpauth/exceptions httpauth/constants).each { |l| require l }

module HTTPAuth
  # = Basic
  #
  # The Basic class provides a number of methods to handle HTTP Basic Authentication. In Basic Authentication
  # the server sends a challenge and the client has to respond to that with the correct credentials. These
  # credentials will have to be sent with every request from that point on.
  #
  # == On the server
  #
  # On the server you will have to check the headers for the 'Authorization' header. When you find one unpack
  # it and check it against your database of credentials. If the credentials are wrong you have to return a
  # 401 status message and a challenge, otherwise proceed as normal. The code is meant as an example, not as
  # runnable code.
  #
  #   def check_authentication(request, response)
  #     credentials = HTTPAuth::Basic.unpack_authorization(request['Authorization'])
  #     if ['admin', 'secret'] == credentials
  #       response.status = 200
  #       return true
  #     else
  #       response.status = 401
  #       response['WWW-Authenticate'] = HTTPAuth::Basic.pack_challenge('Admin Pages')
  #       return false
  #     end
  #   end
  #
  # == On the client
  #
  # On the client you have to detect the WWW-Authenticate header sent from the server. Once you find one you _should_
  # send credentials for that resource any resource 'deeper in the URL space'. You _may_ send the credentials for
  # every request without a WWW-Authenticate challenge. Note that credentials are valid for a realm, a server can
  # use multiple realms for different resources. The code is meant as an example, not as runnable code.
  #
  #   def get_credentials_from_user_for(realm)
  #     if realm == 'Admin Pages'
  #      return ['admin', 'secret']
  #     else
  #      return [nil, nil]
  #     end
  #   end
  #
  #   def handle_authentication(response, request)
  #     unless response['WWW-Authenticate'].nil?
  #       realm = HTTPAuth::Basic.unpack_challenge(response['WWW-Authenticate])
  #       @credentials[realm] ||= get_credentials_from_user_for(realm)
  #       @last_realm = realm
  #     end
  #     unless @last_realm.nil?
  #       request['Authorization'] = HTTPAuth::Basic.pack_authorization(*@credentials[@last_realm])
  #     end
  #   end
  class Basic
    class << self
      # Unpacks the HTTP Basic 'Authorization' credential header
      #
      # * <tt>authorization</tt>: The contents of the Authorization header
      # * Returns a list with two items: the username and password
      def unpack_authorization(authorization)
        d = authorization.split ' '
        fail(ArgumentError, 'HTTPAuth::Basic can only unpack Basic Authentication headers') unless d[0] == 'Basic'
        Base64.decode64(d[1]).split(':')[0..1]
      end

      # Packs HTTP Basic credentials to an 'Authorization' header
      #
      # * <tt>username</tt>: A string with the username
      # * <tt>password</tt>: A string with the password
      def pack_authorization(username, password)
        format('Basic %s', Base64.encode64("#{username}:#{password}").gsub("\n", ''))
      end

      # Returns contents for the WWW-authenticate header
      #
      # * <tt>realm</tt>: A string with a recognizable title for the restricted resource
      def pack_challenge(realm)
        format("Basic realm=\"%s\"", realm.gsub('"', ''))
      end

      # Returns the name of the realm in a WWW-Authenticate header
      #
      # * <tt>authenticate</tt>: The contents of the WWW-Authenticate header
      def unpack_challenge(authenticate)
        if authenticate =~ /Basic\srealm=\"([^\"]*)\"/
          return Regexp.last_match[1]
        else
          if authenticate =~ /^Basic/
            fail(UnwellformedHeader, "Can't parse the WWW-Authenticate header, it's probably not well formed")
          else
            fail(ArgumentError, 'HTTPAuth::Basic can only unpack Basic Authentication headers')
          end
        end
      end

      # Finds and unpacks the authorization credentials in a hash with the CGI enviroment. Returns [nil,nil] if no
      # credentials were found. See HTTPAuth::CREDENTIAL_HEADERS for supported variable names.
      #
      # _Note for Apache_: normally the Authorization header can be found in the HTTP_AUTHORIZATION env variable,
      # but Apache's mod_auth removes the variable from the enviroment. You can work around this by renaming
      # the variable in your apache configuration (or .htaccess if allowed). For example: rewrite the variable
      # for every request on /admin/*.
      #
      #   RewriteEngine on
      #   RewriteRule ^admin/ - [E=X-HTTP-AUTHORIZATION:%{HTTP:Authorization}]
      def get_credentials(env)
        d = HTTPAuth::CREDENTIAL_HEADERS.inject(false) { |a, e| env[e] || a }
        return unpack_authorization(d) unless !d || d.nil? || d.empty?
        [nil, nil]
      end
    end
  end
end