Skip to content

Commit 2c34c03

Browse files
committed
🚧 username vs authzid vs authcid
1 parent 36d13da commit 2c34c03

8 files changed

+240
-131
lines changed

lib/net/imap/sasl/anonymous_authenticator.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,17 @@ class AnonymousAuthenticator
2121
attr_reader :anonymous_message
2222

2323
# :call-seq:
24-
# new(anonymous_message = "", **) -> authenticator
2524
# new(anonymous_message: "", **) -> authenticator
25+
# new(anonymous_message = "", **) -> authenticator
2626
#
2727
# Creates an Authenticator for the "+ANONYMOUS+" SASL mechanism, as
2828
# specified in RFC-4505[https://tools.ietf.org/html/rfc4505]. To use
2929
# this, see Net::IMAP#authenticate or your client's authentication
3030
# method.
3131
#
32-
# #anonymous_message is an optional message which is sent to the server.
33-
# It may be sent as a positional argument or as a keyword argument.
32+
# ==== Parameters
33+
#
34+
# * _optional_ #anonymous_message — a message to send to the server.
3435
#
3536
# Any other keyword arguments are silently ignored.
3637
def initialize(anon_msg = nil, anonymous_message: nil, **)

lib/net/imap/sasl/digest_md5_authenticator.rb

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Net::IMAP::SASL::DigestMD5Authenticator
2424
# "Authentication identity" is the generic term used by
2525
# RFC-4422[https://tools.ietf.org/html/rfc4422].
2626
# RFC-4616[https://tools.ietf.org/html/rfc4616] and many later RFCs abbreviate
27-
# that to +authcid+. So +authcid+ is available as an alias for #username.
27+
# this to +authcid+.
2828
attr_reader :username
2929
alias authcid username
3030

@@ -101,58 +101,61 @@ class Net::IMAP::SASL::DigestMD5Authenticator
101101
attr_reader :qop
102102

103103
# :call-seq:
104-
# new(username, password, authzid = nil, **options) -> authenticator
104+
# new(authcid:, password:, authzid: nil, **options) -> authenticator
105105
# new(username:, password:, authzid: nil, **options) -> authenticator
106+
# new(username, password, authzid = nil, **options) -> authenticator
106107
#
107108
# Creates an Authenticator for the "+DIGEST-MD5+" SASL mechanism.
108109
#
109110
# Called by Net::IMAP#authenticate and similar methods on other clients.
110111
#
111112
# ==== Parameters
112113
#
113-
# * #username — Identity whose #password is used.
114-
# * #password — A password or passphrase associated with this #username.
115-
# * #authzid ― Alternate identity to act as or on behalf of. Optional.
116-
# * #realm — A namespace for the #username, e.g. a domain. <em>Defaults to the
117-
# last realm in the server-provided .</em>
118-
# * #host — FQDN for requested service. <em>Defaults to</em> #realm.
119-
# * #service_name — The generic host name when the server is replicated.
120-
# * #service — the registered service protocol. e.g. "imap", "smtp", "ldap",
121-
# "xmpp". <em>For Net::IMAP, this defaults to "imap".</em>
122-
# * +warn_deprecation+ — Set to +false+ to silence the warning.
114+
# * #username ― Authentication identity that is associated with #password.
115+
# * #authcid ― An alias for +username+.
116+
# * #password ― A password or passphrase associated with this #username.
117+
# * _optional_ #authzid ― Authorization identity to act as or on behalf of.
118+
# * _optional_ $realm — A namespace for the #username, e.g. a domain.
119+
# <em>Defaults to the last realm in the server-provided .</em>
120+
# * _optional_ #host — FQDN for requested service.
121+
# <em>Defaults to</em> #realm
122+
# * _optional_ #service_name — The generic host name, when the server is
123+
# replicated.
124+
# * _optional_ #service — the registered service protocol. e.g. "imap",
125+
# "smtp", "ldap", "xmpp". <em>For Net::IMAP, this defaults to "imap".</em>
126+
# * _optional_ +warn_deprecation+ — Set to +false+ to silence the warning.
127+
#
128+
# Any other keyword arguments are silently ignored.
129+
#
130+
# When +authzid+ is not set, the server should derive the authorization
131+
# identity from the authentication identity.
123132
#
124133
# See the documentation for each attribute for more details.
125-
def initialize(username_arg = nil, password_arg = nil, authzid_arg = nil,
126-
username: nil, password: nil, authzid: nil,
127-
authcid: nil, # alias for username
128-
realm: nil, service: "imap", host: nil, service_name: nil,
134+
def initialize(user = nil, pass = nil, authz = nil,
135+
authcid: nil, username: nil,
136+
authzid: nil,
137+
password: nil,
138+
realm: nil,
139+
service: "imap",
140+
host: nil,
141+
service_name: nil,
129142
warn_deprecation: true,
130143
**)
144+
@username = authcid || username || user
145+
@password = password || pass
146+
@authzid = authzid || authz
147+
@realm = realm
148+
@host = host
149+
@service = service
150+
@service_name = service_name
151+
@username or raise ArgumentError, "missing username (authcid)"
152+
@password or raise ArgumentError, "missing password"
131153
if warn_deprecation
132154
warn "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC-6331."
133155
end
134-
135156
require "digest/md5"
136157
require "securerandom"
137158
require "strscan"
138-
139-
@username = username || username_arg || authcid
140-
@password = password || password_arg
141-
@authzid = authzid || authzid_arg
142-
@realm = realm
143-
@host = host
144-
@service = service
145-
@service_name = service_name
146-
147-
@username or raise ArgumentError, "missing username"
148-
@password or raise ArgumentError, "missing password"
149-
[username, username_arg, authcid].compact.count == 1 or
150-
raise ArgumentError, "conflicting values for username"
151-
[password, password_arg].compact.count == 1 or
152-
raise ArgumentError, "conflicting values for password"
153-
[authzid, authzid_arg].compact.count <= 1 or
154-
raise ArgumentError, "conflicting values for authzid"
155-
156159
@nc, @stage = {}, STAGE_ONE
157160
end
158161

lib/net/imap/sasl/external_authenticator.rb

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,48 @@ module SASL
1212
# established external to SASL, for example by TLS certificate or IPsec.
1313
class ExternalAuthenticator
1414

15-
# Authorization identity: an identity to act as or on behalf of.
15+
# Authorization identity: an identity to act as or on behalf of. The
16+
# identity form is application protocol specific. If not provided or
17+
# left blank, the server derives an authorization identity from the
18+
# authentication identity. The server is responsible for verifying the
19+
# client's credentials and verifying that the identity it associates
20+
# with the client's authentication identity is allowed to act as (or on
21+
# behalf of) the authorization identity.
22+
#
23+
# For example, an administrator or superuser might take on another role:
24+
#
25+
# imap.authenticate "PLAIN", "root", passwd, authzid: "user"
1626
#
17-
# If not explicitly provided, the server defaults to using the identity
18-
# that was authenticated by the external credentials.
1927
attr_reader :authzid
2028

29+
# An alias for #authzid.
30+
alias username authzid
31+
2132
# :call-seq:
22-
# new(authzid: nil, **) -> authenticator
33+
# new(authzid: nil, **) -> authenticator
34+
# new(username: nil, **) -> authenticator
35+
# new(username = nil, **) -> authenticator
2336
#
2437
# Creates an Authenticator for the "+EXTERNAL+" SASL mechanism, as
2538
# specified in RFC-4422[https://tools.ietf.org/html/rfc4422]. To use
2639
# this, see Net::IMAP#authenticate or your client's authentication
2740
# method.
2841
#
29-
# #authzid is an optional identity to act as or on behalf of.
42+
# ==== Parameters
43+
#
44+
# * _optional_ #authzid ― Authorization identity to act as or on behalf of.
45+
# * _optional_ #username ― An alias for #authzid.
46+
#
47+
# Note that, unlike some other authenticators, the +username+ keyword
48+
# parameter sets the authorization identity and not the authentication
49+
# identity. The authenticated identity is established for the client
50+
# with the OAuth credential.
3051
#
3152
# Any other keyword parameters are quietly ignored.
32-
def initialize(authzid: nil, **)
33-
@authzid = authzid&.to_str&.encode "UTF-8"
34-
if @authzid&.match?(/\u0000/u) # also validates UTF8 encoding
35-
raise ArgumentError, "contains NULL"
36-
end
53+
def initialize(user = nil, authzid: nil, username: nil, **)
54+
@authzid = authzid || username || user
55+
@authzid &&= @authzid.to_str.encode "UTF-8"
56+
raise ArgumentError, "contains NULL" if @authzid&.match?(/\u0000/u)
3757
@done = false
3858
end
3959

lib/net/imap/sasl/oauthbearer_authenticator.rb

Lines changed: 75 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ module SASL
1414
class OAuthAuthenticator
1515
include GS2Header
1616

17-
# Authorization identity: an identity to act as or on behalf of.
17+
# Authorization identity: an identity to act as or on behalf of. The
18+
# identity form is application protocol specific. If not provided or
19+
# left blank, the server derives an authorization identity from the
20+
# authentication identity. The server is responsible for verifying the
21+
# client's credentials and verifying that the identity it associates
22+
# with the client's authentication identity is allowed to act as (or on
23+
# behalf of) the authorization identity.
24+
#
25+
# For example, an administrator or superuser might take on another role:
26+
#
27+
# imap.authenticate "PLAIN", "root", passwd, authzid: "user"
1828
#
19-
# If no explicit authorization identity is provided, it is usually
20-
# derived from the authentication identity. For the OAuth-based
21-
# mechanisms, the authentication identity is the identity established by
22-
# the OAuth credential.
2329
attr_reader :authzid
30+
alias username authzid
2431

2532
# Hostname to which the client connected.
2633
attr_reader :host
@@ -39,6 +46,7 @@ class OAuthAuthenticator
3946

4047
# The query string. (optional)
4148
attr_reader :qs
49+
alias query qs
4250

4351
# Stores the most recent server "challenge". When authentication fails,
4452
# this may hold information about the failure reason, as JSON.
@@ -47,29 +55,44 @@ class OAuthAuthenticator
4755
# Creates an RFC7628[https://tools.ietf.org/html/rfc7628] OAuth
4856
# authenticator.
4957
#
50-
# === Options
58+
# ==== Parameters
59+
#
60+
# See child classes for required parameter(s). The following parameters
61+
# are all optional, but it is worth noting that <b>application protocols
62+
# are allowed to require</b> #authzid (or other parameters, such as
63+
# #host or #port) <b>as are specific server implementations</b>.
5164
#
52-
# See child classes for required configuration parameter(s). The
53-
# following parameters are all optional, but protocols or servers may
54-
# add requirements for #authzid, #host, #port, or any other parameter.
65+
# * _optional_ #authzid ― Authorization identity to act as or on behalf of.
66+
# * _optional_ #username — An alias for #authzid.
5567
#
56-
# * #authzid ― Identity to act as or on behalf of.
57-
# * #host — Hostname to which the client connected.
58-
# * #port — Service port to which the client connected.
59-
# * #mthd — HTTP method
60-
# * #path — HTTP path data
61-
# * #post — HTTP post data
62-
# * #qs — HTTP query string
68+
# Note that, unlike some other authenticators, the +username+ keyword
69+
# parameter sets the authorization identity and not the authentication
70+
# identity. The authenticated identity is established for the client
71+
# with the #oauth2_token.
72+
# * _optional_ #host — Hostname to which the client connected.
73+
# * _optional_ #port — Service port to which the client connected.
74+
# * _optional_ #mthd — HTTP method
75+
# * _optional_ #path — HTTP path data
76+
# * _optional_ #post — HTTP post data
77+
# * _optional_ #qs — HTTP query string
78+
# * _optional_ #query — An alias for #qs
6379
#
80+
# Any other keyword parameters are quietly ignored.
81+
#
82+
# Note that, unlike some other authenticators, the +username+ keyword
83+
# parameter sets the authorization identity and not the authentication
84+
# identity. The authenticated identity is established for the client
85+
# with the OAuth credential.
6486
def initialize(authzid: nil, host: nil, port: nil,
87+
username: nil, query: nil,
6588
mthd: nil, path: nil, post: nil, qs: nil, **)
66-
@authzid = authzid
89+
@authzid = authzid || username
6790
@host = host
6891
@port = port
6992
@mthd = mthd
7093
@path = path
7194
@post = post
72-
@qs = qs
95+
@qs = qs || query
7396
@done = false
7497
end
7598

@@ -116,36 +139,48 @@ def authorization; raise "must be implemented by subclass" end
116139
# the bearer token.
117140
class OAuthBearerAuthenticator < OAuthAuthenticator
118141

119-
# An OAuth2 bearer token, generally the access token.
142+
# An OAuth 2.0 bearer token. See {RFC-6750}[https://www.rfc-editor.org/rfc/rfc6750]
120143
attr_reader :oauth2_token
121144

122145
# :call-seq:
123-
# new(oauth2_token, **options) -> authenticator
124-
# new(oauth2_token:, **options) -> authenticator
146+
# new(oauth2_token:, authzid: nil, host: nil, port: nil, **options) -> authenticator
147+
# new(oauth2_token:, username: nil, host: nil, port: nil, **options) -> authenticator
148+
# new(oauth2_token, authzid: nil, host: nil, port: nil, **options) -> authenticator
149+
# new(authzid, oauth2_token, host: nil, port: nil, **options) -> authenticator
125150
#
126151
# Creates an Authenticator for the "+OAUTHBEARER+" SASL mechanism.
127152
#
128153
# Called by Net::IMAP#authenticate and similar methods on other clients.
129154
#
130-
# === Options
131-
#
132-
# Only +oauth2_token+ is required by the mechanism, however protocols
133-
# and servers may add requirements for #authzid, #host, #port, or any
134-
# other parameter.
135-
#
136-
# * #oauth2_token — An OAuth2 bearer token or access token. *Required.*
137-
# May be provided as either regular or keyword argument.
138-
# * #authzid ― Identity to act as or on behalf of.
139-
# * #host — Hostname to which the client connected.
140-
# * #port — Service port to which the client connected.
141-
# * See OAuthAuthenticator documentation for less common parameters.
142-
#
143-
def initialize(oauth2_token_arg = nil, oauth2_token: nil, **args, &blk)
144-
super(**args, &blk) # handles authzid, host, port, etc
145-
oauth2_token && oauth2_token_arg and
146-
raise ArgumentError, "conflicting values for oauth2_token"
147-
@oauth2_token = oauth2_token || oauth2_token_arg or
148-
raise ArgumentError, "missing oauth2_token"
155+
# ==== Parameters
156+
#
157+
# * #oauth2_token — An OAuth2 bearer token
158+
#
159+
# All other keyword parameters are passed to
160+
# {super}[rdoc-ref:OAuthAuthenticator::new]. The most common ones are:
161+
#
162+
# * _optional_ #authzid ― Authorization identity to act as or on behalf of.
163+
# * _optional_ #username — An alias for #authzid.
164+
#
165+
# Note that, unlike some other authenticators, the +username+ keyword
166+
# parameter sets the authorization identity and not the authentication
167+
# identity. The authenticated identity is established for the client
168+
# with the #oauth2_token.
169+
# * _optional_ #host — Hostname to which the client connected.
170+
# * _optional_ #port — Service port to which the client connected.
171+
#
172+
# Only +oauth2_token+ is required by this mechanism, but it is worth
173+
# noting that <b>application protocols are allowed to require</b>
174+
# #authzid (or other parameters, such as #host or #port) <b>as are
175+
# specific server implementations</b>. At the time this was written,
176+
# <em>Gmail and Yahoo Mail both required</em> +authzid+.
177+
#
178+
# See OAuthAuthenticator documentation for other parameters.
179+
def initialize(arg1 = nil, arg2 = nil, oauth2_token: nil, **args, &blk)
180+
username, oauth2_token_arg = arg2.nil? ? [nil, arg1] : [arg1, arg2]
181+
super(username: username, **args, &blk)
182+
@oauth2_token = oauth2_token || oauth2_token_arg
183+
@oauth2_token or raise ArgumentError, "missing oauth2_token"
149184
end
150185

151186
# :call-seq:

0 commit comments

Comments
 (0)