vines 0.1.0

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.
Files changed (119) hide show
  1. data/LICENSE +19 -0
  2. data/README +34 -0
  3. data/Rakefile +55 -0
  4. data/bin/vines +95 -0
  5. data/conf/certs/README +32 -0
  6. data/conf/certs/ca-bundle.crt +3987 -0
  7. data/conf/config.rb +114 -0
  8. data/lib/vines.rb +155 -0
  9. data/lib/vines/command/bcrypt.rb +12 -0
  10. data/lib/vines/command/cert.rb +49 -0
  11. data/lib/vines/command/init.rb +58 -0
  12. data/lib/vines/command/ldap.rb +35 -0
  13. data/lib/vines/command/restart.rb +12 -0
  14. data/lib/vines/command/schema.rb +24 -0
  15. data/lib/vines/command/start.rb +28 -0
  16. data/lib/vines/command/stop.rb +18 -0
  17. data/lib/vines/config.rb +191 -0
  18. data/lib/vines/contact.rb +99 -0
  19. data/lib/vines/daemon.rb +78 -0
  20. data/lib/vines/error.rb +150 -0
  21. data/lib/vines/jid.rb +56 -0
  22. data/lib/vines/kit.rb +23 -0
  23. data/lib/vines/router.rb +125 -0
  24. data/lib/vines/stanza.rb +55 -0
  25. data/lib/vines/stanza/iq.rb +50 -0
  26. data/lib/vines/stanza/iq/auth.rb +18 -0
  27. data/lib/vines/stanza/iq/disco_info.rb +25 -0
  28. data/lib/vines/stanza/iq/disco_items.rb +23 -0
  29. data/lib/vines/stanza/iq/error.rb +16 -0
  30. data/lib/vines/stanza/iq/ping.rb +16 -0
  31. data/lib/vines/stanza/iq/query.rb +10 -0
  32. data/lib/vines/stanza/iq/result.rb +16 -0
  33. data/lib/vines/stanza/iq/roster.rb +153 -0
  34. data/lib/vines/stanza/iq/session.rb +22 -0
  35. data/lib/vines/stanza/iq/vcard.rb +58 -0
  36. data/lib/vines/stanza/message.rb +41 -0
  37. data/lib/vines/stanza/presence.rb +119 -0
  38. data/lib/vines/stanza/presence/error.rb +23 -0
  39. data/lib/vines/stanza/presence/probe.rb +38 -0
  40. data/lib/vines/stanza/presence/subscribe.rb +66 -0
  41. data/lib/vines/stanza/presence/subscribed.rb +64 -0
  42. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  43. data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
  44. data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
  45. data/lib/vines/storage.rb +216 -0
  46. data/lib/vines/storage/couchdb.rb +119 -0
  47. data/lib/vines/storage/ldap.rb +59 -0
  48. data/lib/vines/storage/local.rb +66 -0
  49. data/lib/vines/storage/redis.rb +108 -0
  50. data/lib/vines/storage/sql.rb +174 -0
  51. data/lib/vines/store.rb +51 -0
  52. data/lib/vines/stream.rb +198 -0
  53. data/lib/vines/stream/client.rb +131 -0
  54. data/lib/vines/stream/client/auth.rb +94 -0
  55. data/lib/vines/stream/client/auth_restart.rb +33 -0
  56. data/lib/vines/stream/client/bind.rb +58 -0
  57. data/lib/vines/stream/client/bind_restart.rb +25 -0
  58. data/lib/vines/stream/client/closed.rb +13 -0
  59. data/lib/vines/stream/client/ready.rb +15 -0
  60. data/lib/vines/stream/client/start.rb +27 -0
  61. data/lib/vines/stream/client/tls.rb +37 -0
  62. data/lib/vines/stream/component.rb +53 -0
  63. data/lib/vines/stream/component/handshake.rb +25 -0
  64. data/lib/vines/stream/component/ready.rb +24 -0
  65. data/lib/vines/stream/component/start.rb +19 -0
  66. data/lib/vines/stream/http.rb +111 -0
  67. data/lib/vines/stream/http/http_request.rb +22 -0
  68. data/lib/vines/stream/http/http_state.rb +139 -0
  69. data/lib/vines/stream/http/http_states.rb +53 -0
  70. data/lib/vines/stream/parser.rb +78 -0
  71. data/lib/vines/stream/server.rb +126 -0
  72. data/lib/vines/stream/server/auth.rb +13 -0
  73. data/lib/vines/stream/server/auth_restart.rb +19 -0
  74. data/lib/vines/stream/server/final_restart.rb +20 -0
  75. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  76. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  77. data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
  78. data/lib/vines/stream/server/outbound/final_features.rb +27 -0
  79. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  80. data/lib/vines/stream/server/outbound/start.rb +20 -0
  81. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  82. data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
  83. data/lib/vines/stream/server/ready.rb +20 -0
  84. data/lib/vines/stream/server/start.rb +13 -0
  85. data/lib/vines/stream/server/tls.rb +13 -0
  86. data/lib/vines/stream/state.rb +55 -0
  87. data/lib/vines/token_bucket.rb +46 -0
  88. data/lib/vines/user.rb +124 -0
  89. data/lib/vines/version.rb +5 -0
  90. data/lib/vines/xmpp_server.rb +25 -0
  91. data/test/config_test.rb +396 -0
  92. data/test/error_test.rb +59 -0
  93. data/test/ext/nokogiri.rb +14 -0
  94. data/test/jid_test.rb +71 -0
  95. data/test/kit_test.rb +21 -0
  96. data/test/router_test.rb +60 -0
  97. data/test/stanza/iq/roster_test.rb +198 -0
  98. data/test/stanza/iq/session_test.rb +30 -0
  99. data/test/stanza/iq/vcard_test.rb +159 -0
  100. data/test/stanza/message_test.rb +124 -0
  101. data/test/stanza/presence/subscribe_test.rb +75 -0
  102. data/test/storage/couchdb_test.rb +102 -0
  103. data/test/storage/ldap_test.rb +207 -0
  104. data/test/storage/local_test.rb +54 -0
  105. data/test/storage/redis_test.rb +75 -0
  106. data/test/storage/sql_test.rb +55 -0
  107. data/test/storage/storage_tests.rb +134 -0
  108. data/test/storage_test.rb +90 -0
  109. data/test/stream/client/auth_test.rb +127 -0
  110. data/test/stream/client/ready_test.rb +47 -0
  111. data/test/stream/component/handshake_test.rb +46 -0
  112. data/test/stream/component/ready_test.rb +105 -0
  113. data/test/stream/component/start_test.rb +41 -0
  114. data/test/stream/parser_test.rb +121 -0
  115. data/test/stream/server/outbound/auth_test.rb +77 -0
  116. data/test/stream/server/ready_test.rb +100 -0
  117. data/test/token_bucket_test.rb +24 -0
  118. data/test/user_test.rb +64 -0
  119. metadata +318 -0
data/conf/config.rb ADDED
@@ -0,0 +1,114 @@
1
+ # encoding: UTF-8
2
+
3
+ # This is the Vines XMPP server configuration file. Restart the server with
4
+ # 'vines restart' after updating this file.
5
+
6
+ Vines::Config.configure do
7
+ # Set the logging level to debug, info, warn, error, or fatal. The debug
8
+ # level logs all XML sent and received by the server.
9
+ log :info
10
+
11
+ # Each host element below is a virtual host domain name that this server will
12
+ # service. Hosts can share storage configurations or use separate databases.
13
+ # TLS encryption is mandatory so each host must have a <domain>.crt and
14
+ # <domain>.key file in the conf/certs directory. A self-signed certificate can
15
+ # be generated for a virtual host domain with the 'vines cert <domain.tld>'
16
+ # command. Change the example, 'wonderland.lit', domain name to your actual
17
+ # domain.
18
+ #
19
+ # Shared storage example:
20
+ # host 'verona.lit', 'wonderland.lit' do
21
+ # storage 'fs' do
22
+ # dir 'conf/users'
23
+ # end
24
+ # end
25
+
26
+ host 'wonderland.lit' do
27
+ storage 'fs' do
28
+ dir 'conf/users'
29
+ end
30
+ end
31
+
32
+ # Hosts can use LDAP authentication that overrides the authentication
33
+ # provided by a storage database. If LDAP is in use, passwords are not
34
+ # saved or validated against the storage database. However, all other user
35
+ # information, like rosters, is still saved in the storage database.
36
+ #
37
+ # host 'wonderland.lit' do
38
+ # storage 'fs' do
39
+ # dir 'conf/users'
40
+ # end
41
+ # ldap 'ldap.wonderland.lit', 636 do
42
+ # dn 'cn=Directory Manager'
43
+ # password 'secr3t'
44
+ # basedn 'dc=wonderland,dc=lit'
45
+ # object_class 'person'
46
+ # user_attr 'uid'
47
+ # name_attr 'cn'
48
+ # tls true
49
+ # end
50
+ # end
51
+
52
+ # Configure the client-to-server port. The max_resources_per_account attribute
53
+ # limits how many concurrent connections one user can have to the server.
54
+ client '0.0.0.0', 5222 do
55
+ max_stanza_size 65536
56
+ max_resources_per_account 5
57
+ end
58
+
59
+ # Configure the server-to-server port. The max_stanza_size attribute should be
60
+ # much larger than the setting for client-to-server. Add domain names to the
61
+ # 'hosts' white-list attribute to allow those servers to connect. Any connection
62
+ # attempt from a host not in this list will be denied.
63
+ server '0.0.0.0', 5269 do
64
+ max_stanza_size 131072
65
+ hosts []
66
+ end
67
+
68
+ # Configure the XEP-0124 BOSH port. This allows HTTP clients to connect to
69
+ # the XMPP server.
70
+ http '0.0.0.0', 5280 do
71
+ max_stanza_size 131072
72
+ end
73
+
74
+ # Configure the XEP-0114 external component port. Add entries for each
75
+ # component sub-domain allowed to connect to this server. Components must
76
+ # authenticate with a password.
77
+ component '0.0.0.0', 5347 do
78
+ max_stanza_size 131072
79
+ #components 'tea.wonderland.lit' => 'secr3t',
80
+ # 'cake.wonderland.lit' => 'passw0rd'
81
+ end
82
+ end
83
+
84
+ # Available storage implementations:
85
+
86
+ #storage 'fs' do
87
+ # dir 'conf/users'
88
+ #end
89
+
90
+ #storage 'couchdb' do
91
+ # host 'localhost'
92
+ # port 6984
93
+ # database 'xmpp'
94
+ # tls true
95
+ # username ''
96
+ # password ''
97
+ #end
98
+
99
+ #storage 'redis' do
100
+ # host 'localhost'
101
+ # port 6379
102
+ # database 0
103
+ # password ''
104
+ #end
105
+
106
+ #storage 'sql' do
107
+ # adapter 'postgresql'
108
+ # host 'localhost'
109
+ # port 5432
110
+ # database 'xmpp'
111
+ # username ''
112
+ # password ''
113
+ # pool 5
114
+ #end
data/lib/vines.rb ADDED
@@ -0,0 +1,155 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ NAMESPACES = {
5
+ :stream => 'http://etherx.jabber.org/streams'.freeze,
6
+ :client => 'jabber:client'.freeze,
7
+ :server => 'jabber:server'.freeze,
8
+ :component => 'jabber:component:accept'.freeze,
9
+ :roster => 'jabber:iq:roster'.freeze,
10
+ :non_sasl => 'jabber:iq:auth'.freeze,
11
+ :sasl => 'urn:ietf:params:xml:ns:xmpp-sasl'.freeze,
12
+ :tls => 'urn:ietf:params:xml:ns:xmpp-tls'.freeze,
13
+ :bind => 'urn:ietf:params:xml:ns:xmpp-bind'.freeze,
14
+ :session => 'urn:ietf:params:xml:ns:xmpp-session'.freeze,
15
+ :ping => 'urn:xmpp:ping'.freeze,
16
+ :disco_items => 'http://jabber.org/protocol/disco#items'.freeze,
17
+ :disco_info => 'http://jabber.org/protocol/disco#info'.freeze,
18
+ :http_bind => 'http://jabber.org/protocol/httpbind'.freeze,
19
+ :bosh => 'urn:xmpp:xbosh'.freeze,
20
+ :vcard => 'vcard-temp'.freeze
21
+ }.freeze
22
+
23
+ module Log
24
+ @@logger = nil
25
+ def log
26
+ unless @@logger
27
+ @@logger = Logger.new(STDOUT)
28
+ @@logger.level = Logger::INFO
29
+ @@logger.progname = 'vines'
30
+ @@logger.formatter = Class.new(Logger::Formatter) do
31
+ def initialize
32
+ @time = "%Y-%m-%dT%H:%M:%SZ".freeze
33
+ @fmt = "[%s] %5s -- %s: %s\n".freeze
34
+ end
35
+ def call(severity, time, program, msg)
36
+ @fmt % [time.utc.strftime(@time), severity, program, msg2str(msg)]
37
+ end
38
+ end.new
39
+ end
40
+ @@logger
41
+ end
42
+ end
43
+ end
44
+
45
+ %w[
46
+ resolv-replace
47
+ active_record
48
+ base64
49
+ bcrypt
50
+ digest/sha1
51
+ em-http
52
+ em-redis
53
+ eventmachine
54
+ fiber
55
+ logger
56
+ net/ldap
57
+ nokogiri
58
+ openssl
59
+ socket
60
+ thin
61
+ uri
62
+ yaml
63
+
64
+ vines/jid
65
+ vines/stanza
66
+ vines/stanza/iq
67
+ vines/stanza/iq/query
68
+ vines/stanza/iq/auth
69
+ vines/stanza/iq/disco_info
70
+ vines/stanza/iq/disco_items
71
+ vines/stanza/iq/error
72
+ vines/stanza/iq/ping
73
+ vines/stanza/iq/result
74
+ vines/stanza/iq/roster
75
+ vines/stanza/iq/session
76
+ vines/stanza/iq/vcard
77
+ vines/stanza/message
78
+ vines/stanza/presence
79
+ vines/stanza/presence/error
80
+ vines/stanza/presence/probe
81
+ vines/stanza/presence/subscribe
82
+ vines/stanza/presence/subscribed
83
+ vines/stanza/presence/unavailable
84
+ vines/stanza/presence/unsubscribe
85
+ vines/stanza/presence/unsubscribed
86
+
87
+ vines/storage
88
+ vines/storage/couchdb
89
+ vines/storage/ldap
90
+ vines/storage/local
91
+ vines/storage/redis
92
+ vines/storage/sql
93
+
94
+ vines/store
95
+ vines/contact
96
+ vines/config
97
+ vines/daemon
98
+ vines/error
99
+ vines/kit
100
+ vines/router
101
+ vines/token_bucket
102
+ vines/user
103
+ vines/version
104
+ vines/xmpp_server
105
+
106
+ vines/stream
107
+ vines/stream/state
108
+ vines/stream/parser
109
+
110
+ vines/stream/client
111
+ vines/stream/client/start
112
+ vines/stream/client/tls
113
+ vines/stream/client/auth_restart
114
+ vines/stream/client/auth
115
+ vines/stream/client/bind_restart
116
+ vines/stream/client/bind
117
+ vines/stream/client/ready
118
+ vines/stream/client/closed
119
+
120
+ vines/stream/component
121
+ vines/stream/component/start
122
+ vines/stream/component/handshake
123
+ vines/stream/component/ready
124
+
125
+ vines/stream/http
126
+ vines/stream/http/http_state
127
+ vines/stream/http/http_states
128
+ vines/stream/http/http_request
129
+
130
+ vines/stream/server
131
+ vines/stream/server/start
132
+ vines/stream/server/tls
133
+ vines/stream/server/auth_restart
134
+ vines/stream/server/auth
135
+ vines/stream/server/final_restart
136
+ vines/stream/server/ready
137
+
138
+ vines/stream/server/outbound/start
139
+ vines/stream/server/outbound/tls
140
+ vines/stream/server/outbound/tls_result
141
+ vines/stream/server/outbound/auth_restart
142
+ vines/stream/server/outbound/auth
143
+ vines/stream/server/outbound/auth_result
144
+ vines/stream/server/outbound/final_restart
145
+ vines/stream/server/outbound/final_features
146
+
147
+ vines/command/bcrypt
148
+ vines/command/cert
149
+ vines/command/init
150
+ vines/command/ldap
151
+ vines/command/restart
152
+ vines/command/schema
153
+ vines/command/start
154
+ vines/command/stop
155
+ ].each {|f| require f }
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Command
5
+ class Bcrypt
6
+ def run(opts)
7
+ raise 'vines bcrypt <clear text>' unless opts[:args].size == 1
8
+ puts BCrypt::Password.create(opts[:args].first)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Command
5
+ class Cert
6
+ def run(opts)
7
+ raise 'vines cert <domain>' unless opts[:args].size == 1
8
+ dir = File.expand_path(File.join(opts[:config], '../certs'))
9
+ create_cert(opts[:args].first, dir)
10
+ end
11
+
12
+ def create_cert(domain, dir)
13
+ domain = domain.downcase
14
+ key = OpenSSL::PKey::RSA.generate(2048)
15
+ ca = OpenSSL::X509::Name.parse("/C=US/ST=Colorado/L=Denver/O=Vines XMPP Server/CN=#{domain}")
16
+ cert = OpenSSL::X509::Certificate.new
17
+ cert.version = 2
18
+ cert.subject = ca
19
+ cert.issuer = ca
20
+ cert.serial = Time.now.to_i
21
+ cert.public_key = key.public_key
22
+ cert.not_before = Time.now - (24 * 60 * 60)
23
+ cert.not_after = Time.now + (365 * 24 * 60 * 60)
24
+
25
+ factory = OpenSSL::X509::ExtensionFactory.new
26
+ factory.subject_certificate = cert
27
+ factory.issuer_certificate = cert
28
+ cert.extensions = [
29
+ %w[basicConstraints CA:TRUE],
30
+ %w[subjectKeyIdentifier hash],
31
+ %w[subjectAltName] << [domain, hostname].map {|n| "DNS:#{n}" }.join(',')
32
+ ].map {|k, v| factory.create_ext(k, v) }
33
+
34
+ cert.sign(key, OpenSSL::Digest::SHA1.new)
35
+
36
+ {'key' => key, 'crt' => cert}.each_pair do |ext, o|
37
+ name = File.join(dir, "#{domain}.#{ext}")
38
+ File.open(name, "w") {|f| f.write(o.to_pem) }
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def hostname
45
+ Socket.gethostbyname(Socket.gethostname).first.downcase
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Command
5
+ class Init
6
+ def run(opts)
7
+ raise 'vines init <domain>' unless opts[:args].size == 1
8
+ domain = opts[:args].first
9
+ dir = File.expand_path(domain)
10
+ raise "Directory already initialized: #{domain}" if File.exists?(dir)
11
+ Dir.mkdir(dir)
12
+
13
+ FileUtils.cp_r(File.expand_path('../../../../conf', __FILE__), dir)
14
+ users, log, pid = %w[conf/users log pid].map do |sub|
15
+ File.join(dir, sub).tap {|subdir| Dir.mkdir(subdir) }
16
+ end
17
+
18
+ create_users(domain, users)
19
+ update_config(domain, File.join(dir, 'conf', 'config.rb'))
20
+ Command::Cert.new.create_cert(domain, File.join(dir, 'conf/certs'))
21
+
22
+ puts "Initialized server directory: #{domain}"
23
+ puts "Run 'cd #{domain} && vines start' to begin"
24
+ end
25
+
26
+ private
27
+
28
+ def update_config(domain, config)
29
+ text = File.read(config)
30
+ File.open(config, 'w') do |f|
31
+ f.write(text.gsub('wonderland.lit', domain.downcase))
32
+ end
33
+ end
34
+
35
+ def create_users(domain, dir)
36
+ password = 'secr3t'
37
+ alice, arthur = %w[alice arthur].map do |jid|
38
+ User.new(:jid => [jid, domain.downcase].join('@'),
39
+ :password => BCrypt::Password.create(password).to_s)
40
+ end
41
+
42
+ [[alice, arthur], [arthur, alice]].each do |user, contact|
43
+ user.roster << Contact.new(
44
+ :jid => contact.jid,
45
+ :name => contact.jid.node.capitalize,
46
+ :subscription => 'both',
47
+ :groups => %w[Buddies])
48
+ end
49
+
50
+ storage = Storage::Local.new { dir(dir) }
51
+ [alice, arthur].each do |user|
52
+ storage.save_user(user)
53
+ puts "Created example user #{user.jid} with password #{password}"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Command
5
+ class Ldap
6
+ def run(opts)
7
+ raise 'vines ldap <domain>' unless opts[:args].size == 1
8
+ require opts[:config]
9
+ domain = opts[:args].first
10
+ unless storage = Config.instance.vhosts[domain]
11
+ raise "#{domain} virtual host not found in conf/config.rb"
12
+ end
13
+ unless storage.ldap?
14
+ raise "LDAP connector not configured for #{domain} virtual host"
15
+ end
16
+ $stdout.write('JID: ')
17
+ jid = $stdin.gets.chomp
18
+ jid = [jid, domain].join('@') unless jid.include?('@')
19
+ $stdout.write('Password: ')
20
+ `stty -echo`
21
+ password = $stdin.gets.chomp
22
+ `stty echo`
23
+ puts
24
+
25
+ begin
26
+ user = storage.ldap.authenticate(jid, password)
27
+ rescue Exception => e
28
+ raise "LDAP connection failed: #{e.message}"
29
+ end
30
+ raise "User not found" unless user
31
+ puts "Found #{user.jid} with name: #{user.name}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Command
5
+ class Restart
6
+ def run(opts)
7
+ Stop.new.run(opts)
8
+ Start.new.run(opts)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Command
5
+ class Schema
6
+ def run(opts)
7
+ raise 'vines schema <domain>' unless opts[:args].size == 1
8
+ require opts[:config]
9
+ domain = opts[:args].first
10
+ unless storage = Config.instance.vhosts[domain]
11
+ raise "#{domain} virtual host not found in conf/config.rb"
12
+ end
13
+ unless storage.respond_to?(:create_schema)
14
+ raise "SQL storage not configured for #{domain} virtual host"
15
+ end
16
+ begin
17
+ storage.create_schema
18
+ rescue Exception => e
19
+ raise "Schema creation failed: #{e.message}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end