|
1 | 1 | # frozen_string_literal: true
|
2 | 2 |
|
3 | 3 | # Authenticator for the "+PLAIN+" SASL mechanism, specified in
|
4 |
| -# RFC4616[https://tools.ietf.org/html/rfc4616]. See Net::IMAP#authenticate. |
| 4 | +# RFC-4616[https://tools.ietf.org/html/rfc4616]. See Net::IMAP#authenticate. |
5 | 5 | #
|
6 | 6 | # +PLAIN+ authentication sends the password in cleartext.
|
7 |
| -# RFC3501[https://tools.ietf.org/html/rfc3501] encourages servers to disable |
| 7 | +# RFC-3501[https://tools.ietf.org/html/rfc3501] encourages servers to disable |
8 | 8 | # cleartext authentication until after TLS has been negotiated.
|
9 |
| -# RFC8314[https://tools.ietf.org/html/rfc8314] recommends TLS version 1.2 or |
| 9 | +# RFC-8314[https://tools.ietf.org/html/rfc8314] recommends TLS version 1.2 or |
10 | 10 | # greater be used for all traffic, and deprecate cleartext access ASAP. +PLAIN+
|
11 | 11 | # can be secured by TLS encryption.
|
12 | 12 | class Net::IMAP::SASL::PlainAuthenticator
|
13 | 13 |
|
| 14 | + ## |
| 15 | + # :call-seq: |
| 16 | + # new(username, password, authzid = nil, **) -> auth_ctx |
| 17 | + # new(authcid:, password:, authzid: nil, **) -> auth_ctx |
| 18 | + # |
| 19 | + # Creates an Authenticator for the "+PLAIN+" SASL mechanism. |
| 20 | + # |
| 21 | + # Called by Net::IMAP#authenticate and similar methods on other clients. |
| 22 | + # |
| 23 | + # === Properties |
| 24 | + # |
| 25 | + # * #authcid ― Identity whose #password is used. Aliased as #username. |
| 26 | + # * #password ― Password or passphrase associated with this #authcid. |
| 27 | + # * #authzid ― Alternate identity to act as or on behalf of. Optional. |
| 28 | + # |
| 29 | + # See the documentation on each attribute for more details. |
| 30 | + # |
| 31 | + # All three properties may be sent as either positional or keyword arguments. |
| 32 | + def initialize(username_arg = nil, password_arg = nil, authzid_arg = nil, |
| 33 | + authcid: nil, username: nil, password: nil, authzid: nil, **) |
| 34 | + @authcid = authcid || username || username_arg |
| 35 | + @password = password || password_arg |
| 36 | + @authzid = authzid || authzid_arg |
| 37 | + @authcid or raise ArgumentError, "missing authcid (username)" |
| 38 | + @password or raise ArgumentError, "missing password" |
| 39 | + [authcid, username, username_arg].compact.count == 1 or |
| 40 | + raise ArgumentError, "conflicting values for authcid (username)" |
| 41 | + [password, password_arg].compact.count == 1 or |
| 42 | + raise ArgumentError, "conflicting values for password (username)" |
| 43 | + [authzid, authzid_arg].compact.count <= 1 or |
| 44 | + raise ArgumentError, "conflicting values for authzid (username)" |
| 45 | + guard_against_null_char(:authcid, @authcid) |
| 46 | + guard_against_null_char(:authzid, @authzid) |
| 47 | + guard_against_null_char(:password, @password) |
| 48 | + end |
| 49 | + |
| 50 | + # :call-seq: |
| 51 | + # initial_response? -> true |
| 52 | + # |
| 53 | + # +PLAIN+ can send an initial client response. |
14 | 54 | def initial_response?; true end
|
15 | 55 |
|
16 |
| - def process(data) |
17 |
| - return "#@authzid\0#@username\0#@password" |
18 |
| - end |
| 56 | + # Authentication identity: the identity that matches the #password. |
| 57 | + # |
| 58 | + # RFC-2831[https://tools.ietf.org/html/rfc2831] uses the term +username+. |
| 59 | + # "Authentication identity" is the generic term used by |
| 60 | + # RFC-4422[https://tools.ietf.org/html/rfc4422]. |
| 61 | + # RFC-4616[https://tools.ietf.org/html/rfc4616] and many later RFCs abbreviate |
| 62 | + # to +authcid+. #username is available as an alias for #authcid, but only |
| 63 | + # <tt>:authcid</tt> will be sent to callbacks. |
| 64 | + attr_reader :authcid |
| 65 | + alias username authcid |
19 | 66 |
|
20 |
| - # :nodoc: |
21 |
| - NULL = -"\0".b |
| 67 | + # A password or passphrase that matches the #authcid. |
| 68 | + attr_reader :password |
| 69 | + |
| 70 | + # Authorization identity: an identity to act as or on behalf of. The identity |
| 71 | + # form is application protocol specific. If not provided or left blank, the |
| 72 | + # server derives an authorization identity from the authentication identity. |
| 73 | + # The server is responsible for verifying the client's credentials and |
| 74 | + # verifying that the identity it associates with the client's authentication |
| 75 | + # identity is allowed to act as (or on behalf of) the authorization identity. |
| 76 | + # |
| 77 | + # For example, an administrator or superuser might take on another role: |
| 78 | + # |
| 79 | + # imap.authenticate "PLAIN", "root", passwd, authzid: "user" |
| 80 | + # |
| 81 | + attr_reader :authzid |
| 82 | + |
| 83 | + # Responds with the client's credentials. |
| 84 | + def process(data) [authzid, authcid, password].join("\0") end |
22 | 85 |
|
23 | 86 | private
|
24 | 87 |
|
25 |
| - # +username+ is the authentication identity, the identity whose +password+ is |
26 |
| - # used. +username+ is referred to as +authcid+ by |
27 |
| - # RFC4616[https://tools.ietf.org/html/rfc4616]. |
28 |
| - # |
29 |
| - # +authzid+ is the authorization identity (identity to act as). It can |
30 |
| - # usually be left blank. When +authzid+ is left blank (nil or empty string) |
31 |
| - # the server will derive an identity from the credentials and use that as the |
32 |
| - # authorization identity. |
33 |
| - def initialize(username, password, authzid: nil) |
34 |
| - raise ArgumentError, "username contains NULL" if username&.include?(NULL) |
35 |
| - raise ArgumentError, "password contains NULL" if password&.include?(NULL) |
36 |
| - raise ArgumentError, "authzid contains NULL" if authzid&.include?(NULL) |
37 |
| - @username = username |
38 |
| - @password = password |
39 |
| - @authzid = authzid |
| 88 | + NULL = -"\0".b |
| 89 | + private_constant :NULL |
| 90 | + |
| 91 | + def guard_against_null_char(name, value) |
| 92 | + raise ArgumentError, "#{name} contains NULL" if value&.include?(NULL) |
40 | 93 | end
|
41 | 94 |
|
42 | 95 | end
|
0 commit comments