mroch-authpipe 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION.yml +4 -0
- data/lib/authpipe.rb +9 -0
- data/lib/authpipe/account_data.rb +49 -0
- data/lib/authpipe/auth.rb +80 -0
- data/lib/authpipe/enumerate.rb +39 -0
- data/lib/authpipe/passwd.rb +45 -0
- data/lib/authpipe/pre.rb +46 -0
- data/test/authpipe/account_data_test.rb +98 -0
- data/test/authpipe/auth_test.rb +68 -0
- data/test/authpipe/enumerate_test.rb +29 -0
- data/test/authpipe/passwd_test.rb +31 -0
- data/test/authpipe/pre_test.rb +32 -0
- data/test/test_helper.rb +6 -0
- metadata +68 -0
data/VERSION.yml
ADDED
data/lib/authpipe.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Extends a Hash to construct the authpipe response.
|
2
|
+
#
|
3
|
+
# Returns the account data as a series of ATTR=value newline-terminated lines,
|
4
|
+
# followed by a period on a line of its own. Valid attributes are:
|
5
|
+
#
|
6
|
+
# USERNAME=username -- system account which owns mailbox (name)
|
7
|
+
# UID=uid -- system account which owns mailbox (numeric uid)
|
8
|
+
# GID=gid -- numeric groupid
|
9
|
+
# HOME=homedir -- home directory
|
10
|
+
# ADDRESS=addr -- e-mail address
|
11
|
+
# NAME=name -- full name
|
12
|
+
# MAILDIR=maildir -- Maildir relative to home directory
|
13
|
+
# QUOTA=quota -- quota string: maxbytesS,maxfilesC
|
14
|
+
# PASSWD=cryptpasswd -- encrypted password
|
15
|
+
# PASSWD2=plainpasswd -- plain text password
|
16
|
+
# OPTIONS=acctoptions -- option1=val1,option2=val2,...
|
17
|
+
# .
|
18
|
+
#
|
19
|
+
# Of these, it is mandatory to return ADDRESS, HOME, GID, and either UID or
|
20
|
+
# USERNAME; the others are optional.
|
21
|
+
#
|
22
|
+
|
23
|
+
module Authpipe
|
24
|
+
class InvalidAccountData < AuthpipeException ; end
|
25
|
+
|
26
|
+
class AccountData < Hash
|
27
|
+
def to_authpipe
|
28
|
+
validate!
|
29
|
+
result = self.inject([]) do |result, (key, value)|
|
30
|
+
(result << key.to_s.upcase + "=" + value.to_s) unless value.nil?
|
31
|
+
result
|
32
|
+
end
|
33
|
+
result.sort!
|
34
|
+
result << ".\n"
|
35
|
+
return result.join("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_enumerate
|
39
|
+
[self[:username], self[:uid], self[:gid], self[:home], self[:maildir], self[:options]].join("\t")
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate!
|
43
|
+
raise InvalidAccountData, 'ADDRESS is required' unless self[:address]
|
44
|
+
raise InvalidAccountData, 'HOME is required' unless self[:home]
|
45
|
+
raise InvalidAccountData, 'GID is required' unless self[:gid]
|
46
|
+
raise InvalidAccountData, 'Either UID or USERNAME is required' unless self[:uid] || self[:username]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# AUTH <len>\n<len-bytes>
|
2
|
+
#
|
3
|
+
# Validate a login attempt. The AUTH line is followed by len-bytes of
|
4
|
+
# authentication data, which does not necessarily end with a newline. The
|
5
|
+
# currently defined authentication requests are:
|
6
|
+
#
|
7
|
+
# login \n username \n password [\n] -- plaintext login
|
8
|
+
# cram-md5 \n challenge \n response [\n] -- base-64 encoded challenge and response
|
9
|
+
# cram-sha1 \n challenge \n response [\n] -- ditto
|
10
|
+
# cram-sha256 \n challenge \n response [\n] -- ditto
|
11
|
+
#
|
12
|
+
# In the case of success, return the complete set of account parameters in the
|
13
|
+
# same format as PRE, ending with a period on a line of its own. In the case of
|
14
|
+
# failure (e.g. username does not exist, password wrong, unsupported
|
15
|
+
# authentication type), return FAIL<newline>. If there is a temporary failure,
|
16
|
+
# such as a database being down, authProg should terminate without sending any
|
17
|
+
# response.
|
18
|
+
#
|
19
|
+
# Note: if the user provides a plaintext password and authenticates
|
20
|
+
# successfully, then you can return it as PASSWD2 (plain text password) even if
|
21
|
+
# the database contains an encrypted password. This is useful when using the
|
22
|
+
# POP3/IMAP proxy functions of courier-imap.
|
23
|
+
#
|
24
|
+
|
25
|
+
require 'readbytes'
|
26
|
+
|
27
|
+
module Authpipe
|
28
|
+
class Auth
|
29
|
+
|
30
|
+
def self.process(request)
|
31
|
+
Auth.new.process(request)
|
32
|
+
end
|
33
|
+
|
34
|
+
def process(request)
|
35
|
+
params = parse_request(request)
|
36
|
+
if (account = get_account_data(params))
|
37
|
+
return account.to_authpipe
|
38
|
+
else
|
39
|
+
raise AuthpipeException, "AUTH failed"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
# Authenticates the user and returns its account data in an
|
45
|
+
# Authpipe::AccountData object, or returns nil if the user isn't found
|
46
|
+
# or authentication fails.
|
47
|
+
def get_account_data(params)
|
48
|
+
raise NotImplementedError, 'get_account_data must be overridden by subclass'
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_request(request)
|
52
|
+
data = STDIN.readbytes(request.to_i)
|
53
|
+
authservice, authtype, field1, field2 = data.split(/\n/)
|
54
|
+
case authtype
|
55
|
+
when 'login':
|
56
|
+
{ :authservice => authservice, :authtype => 'login', :username => field1, :password => field2 }
|
57
|
+
when 'cram-md5', 'cram-sha1', 'cram-sha256':
|
58
|
+
{ :authservice => authservice, :authtype => authtype, :challenge => field1, :response => field2 }
|
59
|
+
else
|
60
|
+
raise UnsupportedAuthenticationType.new(authtype)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# Exception raised when an unsupported authentication mechanism is requested.
|
67
|
+
class UnsupportedAuthenticationType < AuthpipeException
|
68
|
+
def initialize(authtype)
|
69
|
+
@authtype = authtype
|
70
|
+
end
|
71
|
+
|
72
|
+
def message
|
73
|
+
if @authtype
|
74
|
+
"'#{@authtype}' is not a supported authentication type. login, cram-md5, cram-sha1 and cram-sha256 are supported."
|
75
|
+
else
|
76
|
+
"Unsupported authentication type. login, cram-md5, cram-sha1 and cram-sha256 are supported."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# ENUMERATE \n
|
2
|
+
#
|
3
|
+
# Return a list of all accounts, one per line in the following format, ending
|
4
|
+
# with a period on a line of its own:
|
5
|
+
#
|
6
|
+
# username \t uid \t gid \t homedir \t maildir \t options \n
|
7
|
+
# .
|
8
|
+
#
|
9
|
+
# If your module does not support the ENUMERATE command then return just a
|
10
|
+
# period on a line of its own (which will still allow enumeration data from
|
11
|
+
# other modules to be returned). In the case of a temporary failure, such as a
|
12
|
+
# database being down or an error occuring mid-way through returning account
|
13
|
+
# data, authProg should terminate before sending the terminating period.
|
14
|
+
#
|
15
|
+
|
16
|
+
module Authpipe
|
17
|
+
class Enumerate
|
18
|
+
|
19
|
+
def self.process(request = nil)
|
20
|
+
Pre.new.process(request)
|
21
|
+
end
|
22
|
+
|
23
|
+
def process(request = nil)
|
24
|
+
if (accounts = get_account_data) && !accounts.empty?
|
25
|
+
return accounts.collect { |a| a.to_enumerate }.join("\n") + "\n."
|
26
|
+
else
|
27
|
+
return '.'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
# Retrieves account data for the username and authservice given in +params+.
|
33
|
+
# Returns an Authpipe::AccountData object or nil if the user isn't found.
|
34
|
+
def get_account_data
|
35
|
+
raise NotImplementedError, 'get_account_data must be overridden by subclass'
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# PASSWD service<tab> username<tab> oldpasswd<tab> newpasswd<tab> <newline>
|
2
|
+
#
|
3
|
+
# Request a password change for the given account: validate that the
|
4
|
+
# oldpassword is correct, and if so, change it to the newpassword.
|
5
|
+
#
|
6
|
+
# Reply: the string for success, or FAIL<newline> for a data error (e.g. no
|
7
|
+
# such account, old password wrong, new password not acceptable). In the case
|
8
|
+
# of a temporary failure, such as a database being down, authProg should
|
9
|
+
# terminate without sending any response.
|
10
|
+
#
|
11
|
+
|
12
|
+
module Authpipe
|
13
|
+
class Passwd
|
14
|
+
|
15
|
+
def self.process(request = nil)
|
16
|
+
Pre.new.process(request)
|
17
|
+
end
|
18
|
+
|
19
|
+
def process(request)
|
20
|
+
params = parse_request(request)
|
21
|
+
if (account = update_account_password(params))
|
22
|
+
return account.to_authpipe
|
23
|
+
else
|
24
|
+
raise AuthpipeException, 'Unable to update password'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
# Looks up the account by :service and :username, then confirms that
|
30
|
+
# :oldpasswd is correct, then changes the password to :newpasswd and
|
31
|
+
# returns the updated Authpipe::AccountData object.
|
32
|
+
def update_account_password(params)
|
33
|
+
raise NotImplementedError, 'update_account_password must be overridden by subclass'
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_request(request)
|
37
|
+
if request.strip =~ /^(\w+)\t(\w+)\t(\w+)\t(\w+)$/
|
38
|
+
{ :authservice => $1, :username => $2, :oldpasswd => $3, :newpasswd => $4 }
|
39
|
+
else
|
40
|
+
raise ArgumentError, "Invalid PASSWD request: #{request}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/authpipe/pre.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# PRE . <authservice> <username> \n
|
2
|
+
#
|
3
|
+
# Look up data for an account. authservice identifies the service the user is
|
4
|
+
# trying to use - e.g. pop3, imap, webmail etc.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# If the account is not known, return FAIL<newline>. If there is a temporary
|
8
|
+
# failure, such as a database being down, authProg should terminate (thereby
|
9
|
+
# closing stdin/stdout) without sending any response. authdaemon will restart
|
10
|
+
# the pipe module for the next request, thus ensuring it is properly
|
11
|
+
# reinitialized.
|
12
|
+
#
|
13
|
+
|
14
|
+
module Authpipe
|
15
|
+
class Pre
|
16
|
+
|
17
|
+
def self.process(request)
|
18
|
+
Pre.new.process(request)
|
19
|
+
end
|
20
|
+
|
21
|
+
def process(request)
|
22
|
+
params = parse_request(request)
|
23
|
+
if (account = get_account_data(params))
|
24
|
+
return account.to_authpipe
|
25
|
+
else
|
26
|
+
raise AuthpipeException, "No account found for service '#{params[:authservice]}' and username '#{params[:username]}'"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
# Retrieves account data for the username and authservice given in +params+.
|
32
|
+
# Returns an Authpipe::AccountData object or nil if the user isn't found.
|
33
|
+
def get_account_data(params)
|
34
|
+
raise NotImplementedError, 'get_account_data must be overridden by subclass'
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_request(request)
|
38
|
+
if request.strip =~ /^\. (\w+) (\w+)$/
|
39
|
+
{ :authservice => $1, :username => $2 }
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Invalid PRE request: #{request}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class AccountDataTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@account1 = Authpipe::AccountData[:username => 'foo', :address => 'foo@example.com', :home => '/home/foo', :gid => 1024, :uid => 1024]
|
7
|
+
@account2 = Authpipe::AccountData[:username => 'bar', :address => 'bar@example.com', :home => '/home/bar', :gid => 1025, :uid => 1025]
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_validate
|
11
|
+
acct = create_account_data
|
12
|
+
assert_nothing_raised { acct.validate! }
|
13
|
+
|
14
|
+
acct = create_account_data(:address => nil)
|
15
|
+
assert_raise(Authpipe::InvalidAccountData) { acct.validate! }
|
16
|
+
|
17
|
+
acct = create_account_data(:home => nil)
|
18
|
+
assert_raise(Authpipe::InvalidAccountData) { acct.validate! }
|
19
|
+
|
20
|
+
acct = create_account_data(:gid => nil)
|
21
|
+
assert_raise(Authpipe::InvalidAccountData) { acct.validate! }
|
22
|
+
|
23
|
+
acct = create_account_data(:uid => nil, :username => nil)
|
24
|
+
assert_raise(Authpipe::InvalidAccountData) { acct.validate! }
|
25
|
+
|
26
|
+
acct = create_account_data(:uid => nil) # OK since username still set
|
27
|
+
assert_nothing_raised { acct.validate! }
|
28
|
+
|
29
|
+
acct = create_account_data(:username => nil) # OK since uid still set
|
30
|
+
assert_nothing_raised { acct.validate! }
|
31
|
+
|
32
|
+
acct = Authpipe::AccountData.new
|
33
|
+
assert_raise(Authpipe::InvalidAccountData) { acct.validate! }
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_to_authpipe
|
37
|
+
assert_equal <<-EOF, create_account_data.to_authpipe
|
38
|
+
ADDRESS=foo@example.com
|
39
|
+
GID=1024
|
40
|
+
HOME=/home/foo
|
41
|
+
MAILDIR=.maildir
|
42
|
+
NAME=Test User
|
43
|
+
OPTIONS=option1=val1,option2=val2
|
44
|
+
PASSWD2=abcdef
|
45
|
+
PASSWD=cryptedpassword
|
46
|
+
QUOTA=1024S,1000C
|
47
|
+
UID=1024
|
48
|
+
USERNAME=foo
|
49
|
+
.
|
50
|
+
EOF
|
51
|
+
|
52
|
+
assert_equal <<-EOF, create_account_data(:maildir => nil).to_authpipe
|
53
|
+
ADDRESS=foo@example.com
|
54
|
+
GID=1024
|
55
|
+
HOME=/home/foo
|
56
|
+
NAME=Test User
|
57
|
+
OPTIONS=option1=val1,option2=val2
|
58
|
+
PASSWD2=abcdef
|
59
|
+
PASSWD=cryptedpassword
|
60
|
+
QUOTA=1024S,1000C
|
61
|
+
UID=1024
|
62
|
+
USERNAME=foo
|
63
|
+
.
|
64
|
+
EOF
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_to_enumerate
|
68
|
+
assert_equal \
|
69
|
+
"foo\t1024\t1024\t/home/foo\t.maildir\toption1=val1,option2=val2",
|
70
|
+
create_account_data.to_enumerate
|
71
|
+
|
72
|
+
assert_equal \
|
73
|
+
"\t1024\t1024\t/home/foo\t.maildir\toption1=val1,option2=val2",
|
74
|
+
create_account_data(:username => nil).to_enumerate
|
75
|
+
|
76
|
+
assert_equal \
|
77
|
+
"foo\t\t1024\t/home/foo\t.maildir\toption1=val1,option2=val2",
|
78
|
+
create_account_data(:uid => nil).to_enumerate
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def create_account_data(params = {})
|
83
|
+
Authpipe::AccountData[{
|
84
|
+
:username => 'foo',
|
85
|
+
:address => 'foo@example.com',
|
86
|
+
:home => '/home/foo',
|
87
|
+
:uid => 1024,
|
88
|
+
:gid => 1024,
|
89
|
+
:name => 'Test User',
|
90
|
+
:maildir => '.maildir',
|
91
|
+
:quota => '1024S,1000C',
|
92
|
+
:passwd => 'cryptedpassword',
|
93
|
+
:passwd2 => 'abcdef',
|
94
|
+
:options => 'option1=val1,option2=val2'
|
95
|
+
}.merge!(params)]
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class AuthTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@handler = Authpipe::Auth.new
|
7
|
+
@account = Authpipe::AccountData[:address => 'foo@example.com', :home => '/home/foo', :gid => 1024, :uid => 1024]
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_process_validates_auth_type
|
11
|
+
auth = "unsupported\nfoo\nbar"
|
12
|
+
STDIN.expects(:readbytes).returns(auth)
|
13
|
+
@handler.expects(:get_account_data).never
|
14
|
+
assert_raises(Authpipe::UnsupportedAuthenticationType) do
|
15
|
+
@handler.process(auth.length)
|
16
|
+
end
|
17
|
+
|
18
|
+
['login', 'cram-md5', 'cram-sha1', 'cram-sha256'].each do |authtype|
|
19
|
+
auth = "#{authtype}\nfoo\nbar"
|
20
|
+
STDIN.expects(:readbytes).returns(auth)
|
21
|
+
@handler.expects(:get_account_data).returns(@account)
|
22
|
+
assert_nothing_raised do
|
23
|
+
@handler.process(auth.length)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_process_fails_auth
|
29
|
+
auth = "login\nfoo\nbar"
|
30
|
+
STDIN.expects(:readbytes).returns(auth)
|
31
|
+
|
32
|
+
@handler.expects(:get_account_data).returns(nil)
|
33
|
+
assert_raises(Authpipe::AuthpipeException) do
|
34
|
+
@handler.process(auth.length)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_process_passes_auth
|
39
|
+
auth = "login\nfoo\nbar"
|
40
|
+
STDIN.expects(:readbytes).returns(auth)
|
41
|
+
|
42
|
+
@handler.expects(:get_account_data).returns(@account)
|
43
|
+
assert_nothing_raised do
|
44
|
+
assert_equal @account.to_authpipe, @handler.process(auth.length)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_process_calls_get_account_data
|
49
|
+
auth = "login\nfoo\nbar"
|
50
|
+
STDIN.expects(:readbytes).returns(auth)
|
51
|
+
@handler.expects(:get_account_data).with(
|
52
|
+
:authtype => 'login', :username => 'foo', :password => 'bar'
|
53
|
+
).returns(@account)
|
54
|
+
assert_nothing_raised do
|
55
|
+
@handler.process(auth.length)
|
56
|
+
end
|
57
|
+
|
58
|
+
auth = "login\nabcdef\nqazwsx"
|
59
|
+
STDIN.expects(:readbytes).returns(auth)
|
60
|
+
@handler.expects(:get_account_data).with(
|
61
|
+
:authtype => 'login', :username => 'abcdef', :password => 'qazwsx'
|
62
|
+
).returns(@account)
|
63
|
+
assert_nothing_raised do
|
64
|
+
@handler.process(auth.length)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class EnumerateTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@handler = Authpipe::Enumerate.new
|
7
|
+
@account1 = Authpipe::AccountData[:username => 'foo', :address => 'foo@example.com', :home => '/home/foo', :gid => 1024, :uid => 1024]
|
8
|
+
@account2 = Authpipe::AccountData[:username => 'bar', :address => 'bar@example.com', :home => '/home/bar', :gid => 1025, :uid => 1025]
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_process_calls_get_account_data
|
12
|
+
@handler.expects(:get_account_data).with()
|
13
|
+
@handler.process
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_process_fails_pre
|
17
|
+
@handler.expects(:get_account_data).returns(nil)
|
18
|
+
assert_equal '.', @handler.process
|
19
|
+
|
20
|
+
@handler.expects(:get_account_data).returns([])
|
21
|
+
assert_equal '.', @handler.process
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_process_passes_pre
|
25
|
+
@handler.expects(:get_account_data).returns([@account1, @account2])
|
26
|
+
assert_equal "#{@account1.to_enumerate}\n#{@account2.to_enumerate}\n.", @handler.process
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class PasswdTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@handler = Authpipe::Passwd.new
|
7
|
+
@account = Authpipe::AccountData[
|
8
|
+
:username => 'foo',
|
9
|
+
:address => 'foo@example.com',
|
10
|
+
:home => '/home/foo',
|
11
|
+
:gid => 1024,
|
12
|
+
:uid => 1024,
|
13
|
+
:passwd2 => 'def'
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_process_fails_pre
|
18
|
+
@handler.expects(:update_account_password).returns(nil)
|
19
|
+
assert_raise(Authpipe::AuthpipeException) do
|
20
|
+
@handler.process("imap\tfoo\tabc\tdef")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_process_passes_pre
|
25
|
+
@handler.expects(:update_account_password).with(
|
26
|
+
{ :authservice => 'imap', :username => 'foo', :oldpasswd => 'abc', :newpasswd => 'def' }
|
27
|
+
).returns(@account)
|
28
|
+
assert_equal @account.to_authpipe, @handler.process("imap\tfoo\tabc\tdef")
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class PreTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@handler = Authpipe::Pre.new
|
7
|
+
@account = Authpipe::AccountData[:address => 'foo@example.com', :home => '/home/foo', :gid => 1024, :uid => 1024]
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_process_calls_get_account_data
|
11
|
+
pre = ". imap foo"
|
12
|
+
@handler.expects(:get_account_data).with(
|
13
|
+
:authservice => 'imap', :username => 'foo'
|
14
|
+
).returns(@account)
|
15
|
+
@handler.process(pre)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_process_fails_pre
|
19
|
+
@handler.expects(:get_account_data).returns(nil)
|
20
|
+
assert_raises(Authpipe::AuthpipeException) do
|
21
|
+
@handler.process(". imap foo")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_process_passes_pre
|
26
|
+
@handler.expects(:get_account_data).returns(@account)
|
27
|
+
assert_nothing_raised do
|
28
|
+
assert_equal @account.to_authpipe, @handler.process(". imap foo")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mroch-authpipe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marshall Roch
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-11 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: mroch@cmu.edu
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/authpipe
|
27
|
+
- lib/authpipe/account_data.rb
|
28
|
+
- lib/authpipe/auth.rb
|
29
|
+
- lib/authpipe/enumerate.rb
|
30
|
+
- lib/authpipe/passwd.rb
|
31
|
+
- lib/authpipe/pre.rb
|
32
|
+
- lib/authpipe.rb
|
33
|
+
- test/authpipe
|
34
|
+
- test/authpipe/account_data_test.rb
|
35
|
+
- test/authpipe/auth_test.rb
|
36
|
+
- test/authpipe/enumerate_test.rb
|
37
|
+
- test/authpipe/passwd_test.rb
|
38
|
+
- test/authpipe/pre_test.rb
|
39
|
+
- test/test_helper.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/mroch/authpipe
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- --inline-source
|
45
|
+
- --charset=UTF-8
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.2.0
|
64
|
+
signing_key:
|
65
|
+
specification_version: 2
|
66
|
+
summary: TODO
|
67
|
+
test_files: []
|
68
|
+
|