gsasl 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/.travis.yml +2 -0
- data/LICENCE +19 -0
- data/README.md +64 -15
- data/lib/gsasl/context.rb +49 -1
- data/lib/gsasl/peer.rb +217 -10
- data/lib/gsasl/remote_authenticator.rb +44 -0
- data/lib/gsasl/version.rb +1 -1
- data/lib/gsasl.rb +1 -0
- data/spec/abstraction_spec.rb +212 -0
- data/spec/authentication_spec.rb +261 -2
- data/spec/context_spec.rb +16 -0
- data/spec/peer_spec.rb +41 -0
- data/spec/remote_authenticator_spec.rb +39 -0
- metadata +12 -3
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ruby-1.9.3-p194
|
data/.travis.yml
ADDED
data/LICENCE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Vincent Landgraf
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -9,33 +9,82 @@ This libaray is a lib ffi based wrapper for the [GNU SASL](http://www.gnu.org/so
|
|
9
9
|
* **CRAM-MD5**: Challenge-Response Authentication Mechanism.
|
10
10
|
* **DIGEST-MD5**: Digest Authentication.
|
11
11
|
* **SCRAM-SHA-1**: SCRAM-SHA-1 authentication.
|
12
|
-
* **NTLM**: Microsoft NTLM authentication.
|
13
12
|
* **SECURID**: Authentication using tokens.
|
13
|
+
|
14
|
+
Platfrom and compile flags dependend mechanisms:
|
15
|
+
|
16
|
+
* **NTLM**: Microsoft NTLM authentication.
|
14
17
|
* **GSSAPI**: GSSAPI (Kerberos 5) authentication.
|
15
18
|
* **GS2-KRB5**: Improved GSSAPI (Kerberos 5) authentication.
|
16
19
|
* **KERBEROS\_V5**: Experimental KERBEROS\_V5 authentication.
|
17
20
|
* **SAML20**: Experimental SAML20 authentication.
|
18
21
|
* **OPENID20**: Experimental OPENID20 authentication.
|
19
22
|
|
23
|
+
# Install libgsasl
|
24
|
+
|
25
|
+
To use the library the libgsasl must be installed on the system. The gem uses libffi to access the library so no further comilation needed. It also should work with all important versions of ruby.
|
26
|
+
|
27
|
+
## Mac OS X
|
28
|
+
|
29
|
+
Install the library using homebrew:
|
30
|
+
|
31
|
+
brew install libgsasl
|
32
|
+
|
33
|
+
## Debian & Ubuntu
|
34
|
+
|
35
|
+
Install the library using apt-get:
|
36
|
+
|
37
|
+
sudo apt-get install libgsasl7
|
38
|
+
|
39
|
+
## FreeBSD
|
40
|
+
|
41
|
+
Install the library using ports (as root):
|
42
|
+
|
43
|
+
cd /usr/ports/security/gsasl/
|
44
|
+
make install clean
|
45
|
+
|
20
46
|
# Use in Ruby
|
21
47
|
|
48
|
+
## To authenticate against a server
|
49
|
+
|
50
|
+
In this example a client authenticates against an IMAP4 server. The methode `#authenticate_with` is used to setup all the data and callbacks neccessary to perform the authentication.
|
51
|
+
|
52
|
+
# connect to an imap server
|
53
|
+
require 'socket'
|
54
|
+
socket = TCPSocket.new('imap.example.com', 143)
|
55
|
+
puts socket.gets
|
56
|
+
|
57
|
+
# issue an authenticate command
|
58
|
+
socket.print "a1 AUTHENTICATE LOGIN\r\n"
|
59
|
+
|
60
|
+
# authenticate using the imap4 protocol specifics
|
61
|
+
context = Gsasl::Context.new
|
62
|
+
context.authenticate_with("LOGIN", "user@example.com", "secret") do |remote|
|
63
|
+
remote.receive { socket.gets.gsub!("\r\n|+\s", "") }
|
64
|
+
remote.send { |data| socket.print "#{data}\r\n" }
|
65
|
+
end
|
66
|
+
|
67
|
+
# logout
|
68
|
+
puts socket.gets
|
69
|
+
socket.print "a2 LOGOUT\r\n"
|
70
|
+
|
71
|
+
# close connection
|
72
|
+
puts socket.gets
|
73
|
+
socket.close
|
74
|
+
|
75
|
+
## Advanced
|
76
|
+
|
22
77
|
In the following example the server and the client are on the same machine. If the server is on the remote site, one has to implement a server that will return the next challenge on `server#read` and implements a `server#send` to send the challenge to the server. Also it is possible to not use the `#authenticate` function but to implement the processing individually.
|
23
78
|
|
24
79
|
session = Gsasl::Context.new
|
25
80
|
client = session.create_client("CRAM-MD5")
|
26
|
-
server = session.create_server("CRAM-MD5")
|
27
|
-
|
28
|
-
server.set_callback do |property|
|
29
|
-
if property == Gsasl::GSASL_PASSWORD
|
30
|
-
if server[Gsasl::GSASL_AUTHID] == "joe"
|
31
|
-
server[Gsasl::GSASL_PASSWORD] = "secret"
|
32
|
-
end
|
33
|
-
Gsasl::GSASL_OK
|
34
|
-
end
|
81
|
+
server = session.create_server("CRAM-MD5") do |type, authid|
|
82
|
+
"secret" if type = :password && authid == "joe"
|
35
83
|
end
|
36
84
|
|
37
|
-
client
|
38
|
-
client
|
39
|
-
|
40
|
-
|
41
|
-
|
85
|
+
@client.credentials!("joe", "secret")
|
86
|
+
@client.authenticate(@server).should be_true
|
87
|
+
|
88
|
+
# Copyright Licence
|
89
|
+
|
90
|
+
Copyright (c) 2012 Vincent Landgraf All Rights Reserved. Released under a MIT License.
|
data/lib/gsasl/context.rb
CHANGED
@@ -60,9 +60,15 @@ module Gsasl
|
|
60
60
|
# @return [Gsasl::Peer] the server peer
|
61
61
|
# @example
|
62
62
|
# peer = @session.create_server("CRAM-MD5")
|
63
|
-
|
63
|
+
# @example Server with password database attached directly
|
64
|
+
# peer = @session.create_server("CRAM-MD5") do |type, authid|
|
65
|
+
# DB.find_password_for_user(auth_id) if type == :password
|
66
|
+
# end
|
67
|
+
def create_server(mechanism_name, realm = "gsasl", &block)
|
64
68
|
peer = Peer.new(@context, mechanism_name, :server)
|
65
69
|
@peers[peer.session.address] = peer
|
70
|
+
peer.realm = realm
|
71
|
+
peer.authentication_callback = block if block_given?
|
66
72
|
peer
|
67
73
|
end
|
68
74
|
|
@@ -76,6 +82,48 @@ module Gsasl
|
|
76
82
|
@peers[peer.session.address] = peer
|
77
83
|
peer
|
78
84
|
end
|
85
|
+
|
86
|
+
# Authenticate against a remote peer using a socket like authenication
|
87
|
+
# scheme.
|
88
|
+
# @param [String] mechanism the SASL mechanism to use
|
89
|
+
# @param [String] authid the username auth id of the user to use for auth.
|
90
|
+
# @param [String] password the password of the specified user
|
91
|
+
# @return [Boolean] true if the authentication was successful, false
|
92
|
+
# otherwise
|
93
|
+
# @yield [remote] the block that defines how to interact with the remote
|
94
|
+
# site
|
95
|
+
# @yieldparam [Gsasl::RemoteAuthenticator] remote the remote authenticator
|
96
|
+
# that needs to be defined in order for gsasl to receive and set data.
|
97
|
+
# @example Authenticate against an imap server with PLAIN authentication
|
98
|
+
# # connect to an imap server
|
99
|
+
# require 'socket'
|
100
|
+
# socket = TCPSocket.new('imap.example.com', 143)
|
101
|
+
# puts socket.gets
|
102
|
+
#
|
103
|
+
# # issue an authenticate command
|
104
|
+
# socket.print "a1 AUTHENTICATE PLAIN\r\n"
|
105
|
+
#
|
106
|
+
# # authenticate using the imap4 protocol specifics
|
107
|
+
# context = Gsasl::Context.new
|
108
|
+
# context.authenticate_with("PLAIN", "user@example.com", "pass") do |remote|
|
109
|
+
# remote.receive { socket.gets.gsub!("\r\n|+\s", "") }
|
110
|
+
# remote.send { |data| socket.print "#{data}\r\n" }
|
111
|
+
# end
|
112
|
+
# puts socket.gets # => capabilities after authentication
|
113
|
+
#
|
114
|
+
# # logout
|
115
|
+
# socket.print "a2 LOGOUT\r\n"
|
116
|
+
# puts socket.gets
|
117
|
+
#
|
118
|
+
# # close connection
|
119
|
+
# socket.close
|
120
|
+
# context.close
|
121
|
+
def authenticate_with(mechanism, authid, password, &block)
|
122
|
+
client = create_client(mechanism)
|
123
|
+
client.credentials!(authid, password)
|
124
|
+
client.authenticate_with(&block)
|
125
|
+
client.close
|
126
|
+
end
|
79
127
|
|
80
128
|
private
|
81
129
|
|
data/lib/gsasl/peer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
1
3
|
module Gsasl
|
2
4
|
# A peer is a client or server side that processes data to do an
|
3
5
|
# authentication.
|
@@ -25,6 +27,46 @@ module Gsasl
|
|
25
27
|
@session = peer.get_pointer(0)
|
26
28
|
end
|
27
29
|
|
30
|
+
# Updates the peer with credential information
|
31
|
+
# @param [String] authid the username auth id of the user to use for auth.
|
32
|
+
# @param [String] password the password of the specified user
|
33
|
+
# @example
|
34
|
+
# peer.credentials! "username", "secret"
|
35
|
+
def credentials!(authid, password)
|
36
|
+
self[Gsasl::GSASL_AUTHID] = authid
|
37
|
+
self[Gsasl::GSASL_PASSWORD] = password
|
38
|
+
end
|
39
|
+
|
40
|
+
# Updates the peer with secure id information
|
41
|
+
# @param [String] authid the username auth id of the user to use for auth.
|
42
|
+
# @param [String] passcode the passcode of the specified id
|
43
|
+
# @example
|
44
|
+
# peer.credentials! "username", "12312312331"
|
45
|
+
def secureid!(authid, passcode)
|
46
|
+
self[Gsasl::GSASL_AUTHID] = authid
|
47
|
+
self[Gsasl::GSASL_PASSCODE] = passcode
|
48
|
+
end
|
49
|
+
|
50
|
+
# Updates the peer with a service definition
|
51
|
+
# @param [String] name the name of the service. a list of service names
|
52
|
+
# can be found here: http://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xml
|
53
|
+
# @param [String] hostname the name of the host the service is on
|
54
|
+
# @example
|
55
|
+
# peer.service! "smtp", "localhost"
|
56
|
+
def service!(name, hostname)
|
57
|
+
self[Gsasl::GSASL_SERVICE] = name
|
58
|
+
self[Gsasl::GSASL_HOSTNAME] = hostname
|
59
|
+
end
|
60
|
+
|
61
|
+
# Update the anoynmous token that the client peer used to authenticate with
|
62
|
+
# the server
|
63
|
+
# @param [String] token the token that will be send to the server
|
64
|
+
# @example
|
65
|
+
# peer.anonymous! "some-token"
|
66
|
+
def anonymous!(token)
|
67
|
+
self[Gsasl::GSASL_ANONYMOUS_TOKEN] = token
|
68
|
+
end
|
69
|
+
|
28
70
|
# Sets a property for this peer.
|
29
71
|
# @param [Fixnum] property on of the Gsasl API Keys
|
30
72
|
# @param [String] value the value tho set for the api key
|
@@ -43,6 +85,53 @@ module Gsasl
|
|
43
85
|
Gsasl.gsasl_property_get(@session, property)
|
44
86
|
end
|
45
87
|
|
88
|
+
# Update the realm of the peer
|
89
|
+
# @param [String] val the realm that should be set on the peer
|
90
|
+
# @example
|
91
|
+
# peer.realm = "Awesome SMTPD"
|
92
|
+
def realm=(val)
|
93
|
+
self[Gsasl::GSASL_REALM] = val
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the realm that is set for the peer
|
97
|
+
# @return [String, nil] the realm if one was set or nil
|
98
|
+
def realm
|
99
|
+
self[Gsasl::GSASL_REALM]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Update authzid for external authentication
|
103
|
+
# @param [String] val the authzid that should be set on the peer
|
104
|
+
# @example
|
105
|
+
# peer.realm = "Awesome SMTPD"
|
106
|
+
def authzid=(val)
|
107
|
+
self[Gsasl::GSASL_AUTHZID] = val
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the authzid for external authentication
|
111
|
+
# @return [String, nil] the authzid if one was set or nil
|
112
|
+
def authzid
|
113
|
+
self[Gsasl::GSASL_AUTHZID]
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the authid for authentication
|
117
|
+
# @return [String, nil] the authid if one was set or nil
|
118
|
+
def authid
|
119
|
+
self[Gsasl::GSASL_AUTHID]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Generate a digest md5 hased password that can be stored in the database
|
123
|
+
# to authenticate the user without saving a plaintext password.
|
124
|
+
# @note if the realm of the peer is set, it will be used to generate the
|
125
|
+
# hash. Therefore it has tp be set later on the peer also to match the
|
126
|
+
# password again. Alternatively the realm can be passed directly.
|
127
|
+
# @param [String] authid the username or id to generate the password for
|
128
|
+
# @param [String] password the password to hash
|
129
|
+
# @param [String] realm the realm if not passed the peer realm will be used
|
130
|
+
# if no realm is set to an empty string ("")
|
131
|
+
def digest_md5_hashed_password(authid, password, realm = nil)
|
132
|
+
Digest::MD5.hexdigest("#{authid}:#{realm || self.realm}:#{password}")
|
133
|
+
end
|
134
|
+
|
46
135
|
# Registers a callback for the peer. In case a variable is not provided.
|
47
136
|
# @yield [property] The callback that will be calles during the processing.
|
48
137
|
# @yieldparam [Fixnum] property a property for the
|
@@ -51,6 +140,98 @@ module Gsasl
|
|
51
140
|
@callback = block
|
52
141
|
end
|
53
142
|
|
143
|
+
# Sets an authentication callback on the peer. The passed block will be
|
144
|
+
# called to determine the authentication password, secureid, password hash
|
145
|
+
# etc.. Depending on what mechanisms one supports different types must be
|
146
|
+
# handled.
|
147
|
+
# @yieldparam [Symbol] type is that should be handled in this case
|
148
|
+
# @yieldparam [String] authid the authenticated id
|
149
|
+
# @yieldreturn [String, true, nil] a mechanism specific value or no value
|
150
|
+
# to indicate a failed authentication
|
151
|
+
def authentication_callback=(block)
|
152
|
+
self.callback do |property|
|
153
|
+
case property
|
154
|
+
when Gsasl::GSASL_PASSWORD
|
155
|
+
handle_password_authentication(&block)
|
156
|
+
when Gsasl::GSASL_VALIDATE_SECURID
|
157
|
+
handle_secureid_authentication(&block)
|
158
|
+
when Gsasl::GSASL_DIGEST_MD5_HASHED_PASSWORD
|
159
|
+
handle_digest_md5_authentication(&block)
|
160
|
+
when Gsasl::GSASL_VALIDATE_ANONYMOUS
|
161
|
+
handle_anonymous_authentication(&block)
|
162
|
+
when Gsasl::GSASL_VALIDATE_EXTERNAL
|
163
|
+
handle_external_authentication(&block)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Handles the password authentication with the passed block. Therefor the
|
169
|
+
# block has to return the password for the passed user. No return value means
|
170
|
+
# that the user is unknown and the authentication fails.
|
171
|
+
# @yield [type, authid] the block that handles password gathering.
|
172
|
+
# @yieldparam [Symbol] type is allways :password for password auth
|
173
|
+
# @yieldparam [String] authid the authenticated id
|
174
|
+
# @yieldreturn [String, nil] the password or nil if the user wasn't found
|
175
|
+
def handle_password_authentication
|
176
|
+
if password = yield(:password, authid)
|
177
|
+
self[Gsasl::GSASL_PASSWORD] = password
|
178
|
+
Gsasl::GSASL_OK
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Handles the digest md5 based password hash authentication with the passed
|
183
|
+
# block. Therefor theblock has to return the hashed password for the passed
|
184
|
+
# user. No return value means that the user is unknown and the
|
185
|
+
# authentication fails.
|
186
|
+
# @yield [type, authid] the block that handles password hash gathering.
|
187
|
+
# @yieldparam [Symbol] type is allways :digest_md5_hashed_password for
|
188
|
+
# digest md5 based password hash auth
|
189
|
+
# @yieldparam [String] authid the authenticated id
|
190
|
+
# @yieldreturn [String, nil] the password hash or nil if the user wasn't
|
191
|
+
# found
|
192
|
+
def handle_digest_md5_authentication
|
193
|
+
if hash = yield(:digest_md5_hashed_password, authid)
|
194
|
+
self[Gsasl::GSASL_DIGEST_MD5_HASHED_PASSWORD] = hash
|
195
|
+
Gsasl::GSASL_OK
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Handles the secureid authentication with the passed block. Therefor the
|
200
|
+
# block has to return the secureid for the passed user. No return value means
|
201
|
+
# that the user is unknown and the authentication fails.
|
202
|
+
# @yield [type, authid] the block that handles secureid gathering.
|
203
|
+
# @yieldparam [Symbol] type is allways :passcode for secureid auth
|
204
|
+
# @yieldparam [String] authid the authenticated id
|
205
|
+
# @yieldreturn [String, nil] the secureid or nil if the user wasn't found
|
206
|
+
def handle_secureid_authentication
|
207
|
+
if secureid = yield(:passcode, authid)
|
208
|
+
self[Gsasl::GSASL_PASSCODE] = secureid
|
209
|
+
Gsasl::GSASL_OK
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Handles the anonymous authentication with the passed block. Therefor the
|
214
|
+
# block has to return the success for the anonymous user. No return value
|
215
|
+
# means that the user is not allowed and the authentication fails.
|
216
|
+
# @yield [type, authid] the block that handles anonymous authentication
|
217
|
+
# @yieldparam [Symbol] type is allways :anonymous for external auth
|
218
|
+
# @yieldparam [String] authid the authenticated id
|
219
|
+
# @yieldreturn [Boolean, nil] true or nil if the anonymous isn't allowed
|
220
|
+
def handle_anonymous_authentication
|
221
|
+
Gsasl::GSASL_OK if yield(:anonymous, self[Gsasl::GSASL_ANONYMOUS_TOKEN])
|
222
|
+
end
|
223
|
+
|
224
|
+
# Handles the external authentication with the passed block. Therefor the
|
225
|
+
# block has to return the success for the passed user. No return value means
|
226
|
+
# that the user is unknown and the authentication fails.
|
227
|
+
# @yield [type, authid] the block that handles external authentication
|
228
|
+
# @yieldparam [Symbol] type is allways :external for external auth
|
229
|
+
# @yieldparam [String] authid the authenticated id
|
230
|
+
# @yieldreturn [Boolean, nil] true or nil if the user wasn't authenticated
|
231
|
+
def handle_external_authentication
|
232
|
+
Gsasl::GSASL_OK if yield(:external, authzid)
|
233
|
+
end
|
234
|
+
|
54
235
|
# Used as a server hook in the local test environment.
|
55
236
|
# @return [Array<Fixnum, String>] Result code and base64 encoded challenge
|
56
237
|
def read #b64_str
|
@@ -69,18 +250,44 @@ module Gsasl
|
|
69
250
|
# @return [Boolean] true if the authentication was successfull,
|
70
251
|
# false otherwise
|
71
252
|
def authenticate(server)
|
72
|
-
result =
|
253
|
+
result = GSASL_NEEDS_MORE
|
254
|
+
input = nil
|
73
255
|
|
74
|
-
|
75
|
-
result,
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
256
|
+
while result == GSASL_NEEDS_MORE
|
257
|
+
result, output = server.send(input)
|
258
|
+
break if result != Gsasl::GSASL_NEEDS_MORE
|
259
|
+
_, input = process output
|
260
|
+
end
|
261
|
+
|
262
|
+
result == Gsasl::GSASL_OK
|
263
|
+
end
|
264
|
+
|
265
|
+
# Authenticate against a remote peer using a socket like authenication
|
266
|
+
# scheme.
|
267
|
+
# @return [Boolean] true if the authentication was successful, false
|
268
|
+
# otherwise
|
269
|
+
# @yield [remote] the block that defines how to interact with the remote
|
270
|
+
# site
|
271
|
+
# @yieldparam [Gsasl::RemoteAuthenticator] remote the remote authenticator
|
272
|
+
# that needs to be defined in order for gsasl to receive and set data.
|
273
|
+
# @example Authenticate with a ruby socket against an imap server
|
274
|
+
# client.authenticate_with do |remote|
|
275
|
+
# remote.receive { socket.gets.gsub!("\r\n|+\s", "") }
|
276
|
+
# remote.send { |data| socket.print "#{data}\r\n" }
|
277
|
+
# end
|
278
|
+
def authenticate_with(&block)
|
279
|
+
result = GSASL_NEEDS_MORE
|
280
|
+
|
281
|
+
# create a new authenticator and define its behaviour
|
282
|
+
remote = RemoteAuthenticator.new
|
283
|
+
block.call(remote)
|
284
|
+
|
285
|
+
while result == GSASL_NEEDS_MORE
|
286
|
+
challenge = remote.receive
|
287
|
+
result, response = process challenge
|
288
|
+
remote.send(response)
|
289
|
+
end
|
82
290
|
|
83
|
-
Gsasl.raise_error!(result) unless Gsasl::GSASL_AUTHENTICATION_ERROR
|
84
291
|
result == Gsasl::GSASL_OK
|
85
292
|
end
|
86
293
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Gsasl
|
2
|
+
# This class handles remote authentication sessions that are based on a socket
|
3
|
+
# like interaction mechanism. This class will most of the time not be used
|
4
|
+
# directly but through helper methods (See Peer#authenticate_with).
|
5
|
+
class RemoteAuthenticator
|
6
|
+
def initialize
|
7
|
+
@receive_callback = nil
|
8
|
+
@send_callback = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
# This defines or calls the recieve callback. It will be defined, if a
|
12
|
+
# block is given, otherwise the callback is going to be called.
|
13
|
+
# @yield the block that is going to be called if data need to be read from
|
14
|
+
# the remote site.
|
15
|
+
# @yieldreturn [String] the callback should return a string that includes
|
16
|
+
# a challenge
|
17
|
+
def receive(&block)
|
18
|
+
if block_given?
|
19
|
+
# define the callback
|
20
|
+
@receive_callback = block
|
21
|
+
elsif @receive_callback
|
22
|
+
@receive_callback.call
|
23
|
+
else
|
24
|
+
raise GsaslError, "The receive callback is not defined!"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# This defines or calls the send callback. It will be defined, if a
|
29
|
+
# block is given, otherwise the callback is going to be called.
|
30
|
+
# @yield [data] the block that is going to be called if data need to be
|
31
|
+
# send to the remote site.
|
32
|
+
# @yieldparam [String] data that should be send to a remote site.
|
33
|
+
def send(data = nil, &block)
|
34
|
+
if block_given?
|
35
|
+
# define the callback
|
36
|
+
@send_callback = block
|
37
|
+
elsif @send_callback
|
38
|
+
@send_callback.call(data)
|
39
|
+
else
|
40
|
+
raise GsaslError, "The send callback is not defined!"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/gsasl/version.rb
CHANGED
data/lib/gsasl.rb
CHANGED
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
describe "Abstraction layer for the authentication callback" do
|
5
|
+
before(:each) do
|
6
|
+
@session = Gsasl::Context.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "ANONYMOUS" do
|
10
|
+
before(:each) do
|
11
|
+
@client = @session.create_client("ANONYMOUS")
|
12
|
+
@server = @session.create_server("ANONYMOUS") do |type, authid|
|
13
|
+
type == :anonymous and authid == "joe"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be able to authenticate with correct credentials" do
|
18
|
+
@client.anonymous! "joe"
|
19
|
+
@client.authenticate(@server).should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not be able to authenticate with wrong credentials" do
|
23
|
+
@client.anonymous! "joe1"
|
24
|
+
@client.authenticate(@server).should be_false
|
25
|
+
end
|
26
|
+
|
27
|
+
after(:each) do
|
28
|
+
@client.close
|
29
|
+
@server.close
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "EXTERNAL" do
|
34
|
+
before(:each) do
|
35
|
+
@client = @session.create_client("EXTERNAL")
|
36
|
+
@server = @session.create_server("EXTERNAL") do |type, authid|
|
37
|
+
type == :external and authid == "joe"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be able to authenticate with correct credentials" do
|
42
|
+
@client.authzid = "joe"
|
43
|
+
@client.authenticate(@server).should be_true
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not be able to authenticate with wrong credentials" do
|
47
|
+
@client.authzid = "joe1"
|
48
|
+
@client.authenticate(@server).should be_false
|
49
|
+
end
|
50
|
+
|
51
|
+
after(:each) do
|
52
|
+
@client.close
|
53
|
+
@server.close
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "PLAIN" do
|
58
|
+
before(:each) do
|
59
|
+
@client = @session.create_client("PLAIN")
|
60
|
+
@server = @session.create_server("PLAIN") do |type, authid|
|
61
|
+
"secret" if type = :password && authid == "joe"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should be able to authenticate with correct credentials" do
|
66
|
+
@client.credentials!("joe", "secret")
|
67
|
+
@client.authenticate(@server).should be_true
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should not be able to authenticate with wrong credentials" do
|
71
|
+
@client.credentials!("joe1", "secret")
|
72
|
+
@client.authenticate(@server).should be_false
|
73
|
+
end
|
74
|
+
|
75
|
+
after(:each) do
|
76
|
+
@client.close
|
77
|
+
@server.close
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "LOGIN" do
|
82
|
+
before(:each) do
|
83
|
+
@client = @session.create_client("LOGIN")
|
84
|
+
@server = @session.create_server("LOGIN") do |type, authid|
|
85
|
+
"secret" if type = :password && authid == "joe"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should be able to authenticate with correct credentials" do
|
90
|
+
@client.credentials!("joe", "secret")
|
91
|
+
@client.authenticate(@server).should be_true
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not be able to authenticate with wrong credentials" do
|
95
|
+
@client.credentials!("joe1", "secret")
|
96
|
+
@client.authenticate(@server).should be_false
|
97
|
+
end
|
98
|
+
|
99
|
+
after(:each) do
|
100
|
+
@client.close
|
101
|
+
@server.close
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "SECURID" do
|
106
|
+
before(:each) do
|
107
|
+
@client = @session.create_client("SECURID")
|
108
|
+
@server = @session.create_server("SECURID") do |type, authid|
|
109
|
+
"579a0eaa23c2c60a1bc5" if type = :passcode && authid == "joe"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should be able to authenticate with correct credentials" do
|
114
|
+
@client.secureid!("joe", "579a0eaa23c2c60a1bc5")
|
115
|
+
@client.authenticate(@server).should be_true
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should not be able to authenticate with wrong credentials" do
|
119
|
+
@client.secureid!("joe1", "579a0eaa23c2c60a1bc5")
|
120
|
+
@client.authenticate(@server).should be_false
|
121
|
+
end
|
122
|
+
|
123
|
+
after(:each) do
|
124
|
+
@client.close
|
125
|
+
@server.close
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "CRAM-MD5" do
|
130
|
+
before(:each) do
|
131
|
+
@client = @session.create_client("CRAM-MD5")
|
132
|
+
@server = @session.create_server("CRAM-MD5") do |type, authid|
|
133
|
+
"secret" if type = :password && authid == "joe"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should be able to authenticate with correct credentials" do
|
138
|
+
@client.credentials!("joe", "secret")
|
139
|
+
@client.authenticate(@server).should be_true
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should not be able to authenticate with wrong credentials" do
|
143
|
+
@client.credentials!("joe1", "secret")
|
144
|
+
@client.authenticate(@server).should be_false
|
145
|
+
end
|
146
|
+
|
147
|
+
after(:each) do
|
148
|
+
@client.close
|
149
|
+
@server.close
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "DIGEST-MD5" do
|
154
|
+
before(:each) do
|
155
|
+
@client = @session.create_client("DIGEST-MD5")
|
156
|
+
@server = @session.create_server("DIGEST-MD5") do |type, authid|
|
157
|
+
# emulate a stored md5 hased password a cleartext password could be used
|
158
|
+
# instead. The format is: <authid:realm:password>
|
159
|
+
if type = :digest_md5_hashed_password && authid == "joe"
|
160
|
+
@server.digest_md5_hashed_password(authid, "secret")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should be able to authenticate with correct credentials" do
|
166
|
+
@client.credentials!("joe", "secret")
|
167
|
+
@client.service!("smtp", "localhost")
|
168
|
+
@client.authenticate(@server).should be_true
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should not be able to authenticate with wrong credentials" do
|
172
|
+
@client.credentials!("joe1", "secret")
|
173
|
+
@client.service!("smtp", "localhost")
|
174
|
+
@client.authenticate(@server).should be_false
|
175
|
+
end
|
176
|
+
|
177
|
+
after(:each) do
|
178
|
+
@client.close
|
179
|
+
@server.close
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "SCRAM-SHA-1" do
|
184
|
+
before(:each) do
|
185
|
+
@client = @session.create_client("SCRAM-SHA-1")
|
186
|
+
@server = @session.create_server("SCRAM-SHA-1") do |type, authid|
|
187
|
+
"secret" if type = :password && authid == "joe"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should be able to authenticate with correct credentials" do
|
192
|
+
@client.credentials!("joe", "secret")
|
193
|
+
@client.service!("smtp", "localhost")
|
194
|
+
@client.authenticate(@server).should be_true
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should not be able to authenticate with wrong credentials" do
|
198
|
+
@client.credentials!("joe1", "secret")
|
199
|
+
@client.service!("smtp", "localhost")
|
200
|
+
@client.authenticate(@server).should be_false
|
201
|
+
end
|
202
|
+
|
203
|
+
after(:each) do
|
204
|
+
@client.close
|
205
|
+
@server.close
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
after(:each) do
|
210
|
+
@session.close
|
211
|
+
end
|
212
|
+
end
|
data/spec/authentication_spec.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'securerandom'
|
2
4
|
|
3
5
|
describe "Authentications" do
|
4
6
|
before(:each) do
|
@@ -41,13 +43,270 @@ describe "Authentications" do
|
|
41
43
|
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
42
44
|
|
43
45
|
@client.authenticate(@server).should be_false
|
44
|
-
end
|
46
|
+
end
|
47
|
+
|
48
|
+
after(:each) do
|
49
|
+
@client.close
|
50
|
+
@server.close
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "DIGEST-MD5" do
|
55
|
+
before(:each) do
|
56
|
+
@client = @session.create_client("DIGEST-MD5")
|
57
|
+
@server = @session.create_server("DIGEST-MD5")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should be able to authenticate correctly" do
|
61
|
+
@server.callback do |property|
|
62
|
+
if property == Gsasl::GSASL_PASSWORD
|
63
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
64
|
+
@server[Gsasl::GSASL_PASSWORD] = "secret"
|
65
|
+
end
|
66
|
+
Gsasl::GSASL_OK
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@client[Gsasl::GSASL_SERVICE] = "imap"
|
71
|
+
@client[Gsasl::GSASL_HOSTNAME] = "localhost"
|
72
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
73
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
74
|
+
|
75
|
+
@client.authenticate(@server).should be_true
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should be possible to not authenticate correctly" do
|
79
|
+
@server.callback do |property|
|
80
|
+
if property == Gsasl::GSASL_PASSWORD
|
81
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
82
|
+
@server[Gsasl::GSASL_PASSWORD] = "test"
|
83
|
+
end
|
84
|
+
Gsasl::GSASL_OK
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
@client[Gsasl::GSASL_SERVICE] = "imap"
|
89
|
+
@client[Gsasl::GSASL_HOSTNAME] = "localhost"
|
90
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
91
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
92
|
+
|
93
|
+
@client.authenticate(@server).should be_false
|
94
|
+
end
|
95
|
+
|
96
|
+
after(:each) do
|
97
|
+
@client.close
|
98
|
+
@server.close
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "SCRAM-SHA-1" do
|
103
|
+
before(:each) do
|
104
|
+
@client = @session.create_client("SCRAM-SHA-1")
|
105
|
+
@server = @session.create_server("SCRAM-SHA-1")
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should be able to authenticate correctly" do
|
109
|
+
@server.callback do |property|
|
110
|
+
if property == Gsasl::GSASL_PASSWORD
|
111
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
112
|
+
@server[Gsasl::GSASL_PASSWORD] = "secret"
|
113
|
+
Gsasl::GSASL_OK
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
119
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
120
|
+
|
121
|
+
@client.authenticate(@server).should be_true
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should be possible to not authenticate correctly" do
|
125
|
+
@server.callback do |property|
|
126
|
+
if property == Gsasl::GSASL_PASSWORD
|
127
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
128
|
+
@server[Gsasl::GSASL_PASSWORD] = "secret"
|
129
|
+
Gsasl::GSASL_OK
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
@client[Gsasl::GSASL_AUTHID] = "joe1"
|
135
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
136
|
+
|
137
|
+
@client.authenticate(@server).should be_false
|
138
|
+
end
|
45
139
|
|
46
140
|
after(:each) do
|
47
141
|
@client.close
|
48
142
|
@server.close
|
49
143
|
end
|
50
|
-
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "PLAIN" do
|
147
|
+
before(:each) do
|
148
|
+
@client = @session.create_client("PLAIN")
|
149
|
+
@server = @session.create_server("PLAIN")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should be able to authenticate correctly" do
|
153
|
+
@server.callback do |property|
|
154
|
+
if property == Gsasl::GSASL_PASSWORD
|
155
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
156
|
+
@server[Gsasl::GSASL_PASSWORD] = "secret"
|
157
|
+
end
|
158
|
+
Gsasl::GSASL_OK
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
163
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
164
|
+
|
165
|
+
@client.authenticate(@server).should be_true
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be possible to not authenticate correctly" do
|
169
|
+
@server.callback do |property|
|
170
|
+
if property == Gsasl::GSASL_PASSWORD
|
171
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
172
|
+
@server[Gsasl::GSASL_PASSWORD] = "test"
|
173
|
+
end
|
174
|
+
Gsasl::GSASL_OK
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
179
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
180
|
+
|
181
|
+
@client.authenticate(@server).should be_false
|
182
|
+
end
|
183
|
+
|
184
|
+
after(:each) do
|
185
|
+
@client.close
|
186
|
+
@server.close
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "LOGIN" do
|
191
|
+
before(:each) do
|
192
|
+
@client = @session.create_client("LOGIN")
|
193
|
+
@server = @session.create_server("LOGIN")
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should be able to authenticate correctly" do
|
197
|
+
@server.callback do |property|
|
198
|
+
if property == Gsasl::GSASL_PASSWORD
|
199
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
200
|
+
@server[Gsasl::GSASL_PASSWORD] = "secret"
|
201
|
+
end
|
202
|
+
Gsasl::GSASL_OK
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
207
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
208
|
+
|
209
|
+
@client.authenticate(@server).should be_true
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should be possible to not authenticate correctly" do
|
213
|
+
@server.callback do |property|
|
214
|
+
if property == Gsasl::GSASL_PASSWORD
|
215
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
216
|
+
@server[Gsasl::GSASL_PASSWORD] = "test"
|
217
|
+
end
|
218
|
+
Gsasl::GSASL_OK
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
223
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
224
|
+
|
225
|
+
@client.authenticate(@server).should be_false
|
226
|
+
end
|
227
|
+
|
228
|
+
after(:each) do
|
229
|
+
@client.close
|
230
|
+
@server.close
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe "SECURID" do
|
235
|
+
before(:each) do
|
236
|
+
@client = @session.create_client("SECURID")
|
237
|
+
@server = @session.create_server("SECURID")
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should be able to authenticate correctly" do
|
241
|
+
@server.callback do |property|
|
242
|
+
if property == Gsasl::GSASL_VALIDATE_SECURID
|
243
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe" &&
|
244
|
+
@server[Gsasl::GSASL_PASSCODE] == "579a0eaa23c2c60a1bc5"
|
245
|
+
Gsasl::GSASL_OK
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
251
|
+
@client[Gsasl::GSASL_PASSCODE] = "579a0eaa23c2c60a1bc5"
|
252
|
+
|
253
|
+
@client.authenticate(@server).should be_true
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should be possible to not authenticate correctly" do
|
257
|
+
@server.callback do |property|
|
258
|
+
if property == Gsasl::GSASL_PASSCODE
|
259
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
260
|
+
@server[Gsasl::GSASL_PASSCODE] = "579a0eaa23c2c60a1bc5"
|
261
|
+
end
|
262
|
+
Gsasl::GSASL_OK
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
267
|
+
@client[Gsasl::GSASL_PASSCODE] = "sekshfkjhcret"
|
268
|
+
|
269
|
+
@client.authenticate(@server).should be_false
|
270
|
+
end
|
271
|
+
|
272
|
+
after(:each) do
|
273
|
+
@client.close
|
274
|
+
@server.close
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe "DIGEST-MD5 with digest md5 based password" do
|
279
|
+
before(:each) do
|
280
|
+
@client = @session.create_client("DIGEST-MD5")
|
281
|
+
@server = @session.create_server("DIGEST-MD5")
|
282
|
+
@server[Gsasl::GSASL_REALM] = "test"
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should be able to authenticate correctly" do
|
286
|
+
@server.callback do |property|
|
287
|
+
if property == Gsasl::GSASL_DIGEST_MD5_HASHED_PASSWORD
|
288
|
+
if @server[Gsasl::GSASL_AUTHID] == "joe"
|
289
|
+
passwd_hash = Digest::MD5.hexdigest("joe:test:secret")
|
290
|
+
@server[Gsasl::GSASL_DIGEST_MD5_HASHED_PASSWORD] = passwd_hash
|
291
|
+
end
|
292
|
+
Gsasl::GSASL_OK
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
@client[Gsasl::GSASL_SERVICE] = "imap"
|
297
|
+
@client[Gsasl::GSASL_HOSTNAME] = "localhost"
|
298
|
+
@client[Gsasl::GSASL_AUTHID] = "joe"
|
299
|
+
@client[Gsasl::GSASL_REALM] = "test"
|
300
|
+
@client[Gsasl::GSASL_PASSWORD] = "secret"
|
301
|
+
|
302
|
+
@client.authenticate(@server).should be_true
|
303
|
+
end
|
304
|
+
|
305
|
+
after(:each) do
|
306
|
+
@client.close
|
307
|
+
@server.close
|
308
|
+
end
|
309
|
+
end
|
51
310
|
|
52
311
|
after(:each) do
|
53
312
|
@session.close
|
data/spec/context_spec.rb
CHANGED
@@ -35,6 +35,22 @@ describe Gsasl::Context do
|
|
35
35
|
@session.client_mechanisms.should include("PLAIN")
|
36
36
|
end
|
37
37
|
|
38
|
+
context "realm" do
|
39
|
+
it "should initialize a server with a default realm" do
|
40
|
+
@server = @session.create_server("PLAIN")
|
41
|
+
@server.realm.should == "gsasl"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should initialize the server with a diffeent realm" do
|
45
|
+
@server = @session.create_server("PLAIN", "test")
|
46
|
+
@server.realm.should == "test"
|
47
|
+
end
|
48
|
+
|
49
|
+
after(:each) do
|
50
|
+
@server.close
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
38
54
|
after(:each) do
|
39
55
|
@session.close
|
40
56
|
end
|
data/spec/peer_spec.rb
CHANGED
@@ -37,6 +37,47 @@ describe Gsasl::Context do
|
|
37
37
|
@peer.call(10)
|
38
38
|
test_property.should == 10
|
39
39
|
end
|
40
|
+
|
41
|
+
it "should to set the credentails" do
|
42
|
+
@peer.credentials!("joe", "secret")
|
43
|
+
@peer[Gsasl::GSASL_AUTHID].should == "joe"
|
44
|
+
@peer[Gsasl::GSASL_PASSWORD].should == "secret"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should to set the secureid" do
|
48
|
+
@peer.secureid!("joe", "123123123123")
|
49
|
+
@peer[Gsasl::GSASL_AUTHID].should == "joe"
|
50
|
+
@peer[Gsasl::GSASL_PASSCODE].should == "123123123123"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should update and return the realm" do
|
54
|
+
@peer.realm.should == nil
|
55
|
+
@peer.realm = "Awesome SMTPD"
|
56
|
+
@peer.realm.should == "Awesome SMTPD"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should be possible to set the service details" do
|
60
|
+
@peer.service!("smtp", "localhost")
|
61
|
+
@peer[Gsasl::GSASL_SERVICE].should == "smtp"
|
62
|
+
@peer[Gsasl::GSASL_HOSTNAME].should == "localhost"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should set the anonymous token for a peer" do
|
66
|
+
@peer.anonymous! "some-token"
|
67
|
+
@peer[Gsasl::GSASL_ANONYMOUS_TOKEN].should == "some-token"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should set and get the authzid fir external authentications" do
|
71
|
+
@peer.authzid.should == nil
|
72
|
+
@peer.authzid = "someid"
|
73
|
+
@peer[Gsasl::GSASL_AUTHZID].should == "someid"
|
74
|
+
@peer.authzid.should == "someid"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should be possible to generate a pre generated md5 hash" do
|
78
|
+
@peer.digest_md5_hashed_password("joe", "secret").should == \
|
79
|
+
"fb2441a715a5484c6fa16147c4a6b7a8"
|
80
|
+
end
|
40
81
|
end
|
41
82
|
|
42
83
|
after(:each) do
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gsasl::RemoteAuthenticator do
|
4
|
+
it "should be possible to create an remote authenticator" do
|
5
|
+
Gsasl::RemoteAuthenticator.new
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with authenticator" do
|
9
|
+
before(:each) do
|
10
|
+
@authenticator = Gsasl::RemoteAuthenticator.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should assign and call the recieve method hock" do
|
14
|
+
a = 0
|
15
|
+
@authenticator.receive { a += 1 }
|
16
|
+
@authenticator.receive.should == 1
|
17
|
+
a.should == 1
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should assign and call the send method hock" do
|
21
|
+
a = nil
|
22
|
+
@authenticator.send { |data| a = data }
|
23
|
+
@authenticator.send("asd").should == "asd"
|
24
|
+
a.should == "asd"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should raise an error if receive is called without being defined" do
|
28
|
+
lambda do
|
29
|
+
@authenticator.receive
|
30
|
+
end.should raise_error(Gsasl::GsaslError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should raise an error if send is called without being defined" do
|
34
|
+
lambda do
|
35
|
+
@authenticator.send
|
36
|
+
end.should raise_error(Gsasl::GsaslError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gsasl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -68,7 +68,10 @@ extra_rdoc_files: []
|
|
68
68
|
files:
|
69
69
|
- .gitignore
|
70
70
|
- .rspec
|
71
|
+
- .rvmrc
|
72
|
+
- .travis.yml
|
71
73
|
- Gemfile
|
74
|
+
- LICENCE
|
72
75
|
- README.md
|
73
76
|
- Rakefile
|
74
77
|
- gsasl.gemspec
|
@@ -76,10 +79,13 @@ files:
|
|
76
79
|
- lib/gsasl/context.rb
|
77
80
|
- lib/gsasl/native.rb
|
78
81
|
- lib/gsasl/peer.rb
|
82
|
+
- lib/gsasl/remote_authenticator.rb
|
79
83
|
- lib/gsasl/version.rb
|
84
|
+
- spec/abstraction_spec.rb
|
80
85
|
- spec/authentication_spec.rb
|
81
86
|
- spec/context_spec.rb
|
82
87
|
- spec/peer_spec.rb
|
88
|
+
- spec/remote_authenticator_spec.rb
|
83
89
|
- spec/spec_helper.rb
|
84
90
|
homepage: ''
|
85
91
|
licenses: []
|
@@ -101,12 +107,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
107
|
version: '0'
|
102
108
|
requirements: []
|
103
109
|
rubyforge_project: gsasl
|
104
|
-
rubygems_version: 1.8.
|
110
|
+
rubygems_version: 1.8.24
|
105
111
|
signing_key:
|
106
112
|
specification_version: 3
|
107
113
|
summary: A lib ffi based wrapper for lib GNU SASL
|
108
114
|
test_files:
|
115
|
+
- spec/abstraction_spec.rb
|
109
116
|
- spec/authentication_spec.rb
|
110
117
|
- spec/context_spec.rb
|
111
118
|
- spec/peer_spec.rb
|
119
|
+
- spec/remote_authenticator_spec.rb
|
112
120
|
- spec/spec_helper.rb
|
121
|
+
has_rdoc:
|