mroch-authpipe 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|