vines 0.1.0

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