1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
##
# RFC 4422:
# http://tools.ietf.org/html/rfc4422
module SASL
##
# You must derive from class Preferences and overwrite methods you
# want to implement.
class Preferences
attr_reader :config
# key in config hash
# authzid: Authorization identitiy ('username@domain' in XMPP)
# realm: Realm ('domain' in XMPP)
# digest-uri: : serv-type/serv-name | serv-type/host/serv-name ('xmpp/domain' in XMPP)
# username
# has_password?
# allow_plaintext?
# password
# want_anonymous?
def initialize (config)
@config = {:has_password? => false, :allow_plaintext? => false, :want_anonymous? => false}.merge(config.dup)
end
def method_missing(sym, *args, &block)
@config.send "[]", sym, &block
end
end
##
# Will be raised by SASL.new_mechanism if mechanism passed to the
# constructor is not known.
class UnknownMechanism < RuntimeError
def initialize(mechanism)
@mechanism = mechanism
end
def to_s
"Unknown mechanism: #{@mechanism.inspect}"
end
end
def SASL.new(mechanisms, preferences)
best_mechanism = if preferences.want_anonymous? && mechanisms.include?('ANONYMOUS')
'ANONYMOUS'
elsif preferences.has_password?
if mechanisms.include?('DIGEST-MD5')
'DIGEST-MD5'
elsif preferences.allow_plaintext?
'PLAIN'
else
raise UnknownMechanism.new(mechanisms)
end
else
raise UnknownMechanism.new(mechanisms)
end
new_mechanism(best_mechanism, preferences)
end
##
# Create a SASL Mechanism for the named mechanism
#
# mechanism:: [String] mechanism name
def SASL.new_mechanism(mechanism, preferences)
mechanism_class = case mechanism
when 'DIGEST-MD5'
DigestMD5
when 'PLAIN'
Plain
when 'ANONYMOUS'
Anonymous
else
raise UnknownMechanism.new(mechanism)
end
mechanism_class.new(mechanism, preferences)
end
class AbstractMethod < Exception # :nodoc:
def to_s
"Abstract method is not implemented"
end
end
##
# Common functions for mechanisms
#
# Mechanisms implement handling of methods start and receive. They
# return: [message_name, content] or nil where message_name is
# either 'auth' or 'response' and content is either a string which
# may transmitted encoded as Base64 or nil.
class Mechanism
attr_reader :mechanism
attr_reader :preferences
def initialize(mechanism, preferences)
@mechanism = mechanism
@preferences = preferences
@state = nil
end
def success?
@state == :success
end
def failure?
@state == :failure
end
def start
raise AbstractMethod
end
def receive(message_name, content)
case message_name
when 'success'
@state = :success
when 'failure'
@state = :failure
end
nil
end
end
end
|