diaspora-vines 0.1.2 → 0.1.21

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a91e59620021a2556bf0d50d4075e0941fb584fe
4
- data.tar.gz: 0f6e7afc2dd98e85c11b4880eda96e724d0db2ba
3
+ metadata.gz: a2e10ea8cd78c49254b4e8dc363d3d23759b40f6
4
+ data.tar.gz: aeedf0672a7578414f0b174b2ab0d61403c0f5b2
5
5
  SHA512:
6
- metadata.gz: 5cc89b54b4ef8b580d80682fb292bc822b41d0cae04202764b2fd34c7c5b0fefd9b92b051f5ea885d2c6c5bb38680853e691221f95776c3c4f68b9debb205524
7
- data.tar.gz: 2075d78c54789a78c626f37800f73b656645ade285f47eacdfd55d55c3f9561668229696c9e418db913a9c4046dfbd63c034a570b52d2776d79601ab2746b299
6
+ metadata.gz: 59a644d8dbd8cab888548ea2d49466ad27d5acc79f99097414d440f3942c72d7ec128ed33df3991841e6d409fca9fccaffc87552925532f0276e4a55e23ead3d
7
+ data.tar.gz: 7c85dcaaa5359bc99714cf24528b7e0f25731681339503e394b3c3856b5457cc81918395a2fce021d8172dc7ec9ef46e0137948e2ac8dcbee7323d87da4c38ba
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  Diaspora XMPP Integration
2
2
  =========================
3
3
 
4
+ **master** [ ![Build Status](https://travis-ci.org/diaspora/vines.svg?branch=master) ](https://travis-ci.org/diaspora/vines)
5
+ **develop** [ ![Build Status](https://travis-ci.org/diaspora/vines.svg?branch=develop) ](https://travis-ci.org/diaspora/vines)
6
+
4
7
  This XMPP server was forked from [Negativecode](http://www.getvines.org/)
5
8
  and was slimmed down for [Diaspora](https://diasporafoundation.org) usage only!
6
9
 
data/conf/config.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  Vines::Config.configure do
2
2
  # Set the logging level to debug, info, warn, error, or fatal. The debug
3
3
  # level logs all XML sent and received by the server.
4
- log :info
4
+ # If you want logging to STDOUT remove the path
5
+ # e.g. `log 'log/vines.log' do` becomes `log do`
6
+ log 'log/vines.log' do
7
+ level :info
8
+ end
5
9
 
6
10
  # Set the directory in which to look for virtual hosts' TLS certificates.
7
11
  # This is optional and defaults to the conf/certs directory created during
8
12
  # `vines init`.
9
- #certs 'config/vines'
13
+ certs 'conf/certs'
10
14
 
11
15
  # Setup a pepper to generate the encrypted password.
12
16
  pepper "065eb8798b181ff0ea2c5c16aee0ff8b70e04e2ee6bd6e08b49da46924223e39127d5335e466207d42bf2a045c12be5f90e92012a4f05f7fc6d9f3c875f4c95b"
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Vines
4
4
  class Cluster
5
- # Subscribes to the redis nodes:all broadcast channel to listen for
5
+ # Subscribes to the redis `nodes:all` broadcast channel to listen for
6
6
  # heartbeats from other cluster members. Also subscribes to a channel
7
7
  # exclusively for this particular node, listening for stanzas routed to us
8
8
  # from other nodes.
@@ -22,6 +22,8 @@ module Vines
22
22
  # Create a new redis connection and subscribe to the nodes:all broadcast
23
23
  # channel as well as the channel for this cluster node. Redis connections
24
24
  # in subscribe mode cannot be used for other key/value operations.
25
+ #
26
+ # Returns nothing.
25
27
  def subscribe
26
28
  conn = @cluster.connect
27
29
  conn.subscribe(ALL)
@@ -35,6 +37,8 @@ module Vines
35
37
 
36
38
  # Recursively process incoming messages from the queue, guaranteeing they
37
39
  # are processed in the order they are received.
40
+ #
41
+ # Returns nothing.
38
42
  def process_messages
39
43
  @messages.pop do |channel, message|
40
44
  Fiber.new do
@@ -46,6 +50,11 @@ module Vines
46
50
 
47
51
  # Process messages as they arrive on the pubsub channels to which we're
48
52
  # subscribed.
53
+ #
54
+ # channel - The String channel name on which the message was received.
55
+ # message - The JSON formatted message String.
56
+ #
57
+ # Returns nothing.
49
58
  def on_message(channel, message)
50
59
  doc = JSON.parse(message)
51
60
  case channel
@@ -56,9 +65,13 @@ module Vines
56
65
  log.error("Cluster subscription message failed: #{e}")
57
66
  end
58
67
 
59
- # Process a message sent to the nodes:all broadcast channel. In the case
68
+ # Process a message sent to the `nodes:all` broadcast channel. In the case
60
69
  # of node heartbeats, we update the last time we heard from this node so
61
70
  # we can cleanup its session if it goes offline.
71
+ #
72
+ # message - The parsed Hash of received message data.
73
+ #
74
+ # Returns nothing.
62
75
  def to_all(message)
63
76
  case message[TYPE]
64
77
  when ONLINE, HEARTBEAT
@@ -71,6 +84,10 @@ module Vines
71
84
  # Process a message published to this node's channel. Messages sent to
72
85
  # this channel are stanzas that need to be routed to connections attached
73
86
  # to this node.
87
+ #
88
+ # message - The parsed Hash of received message data.
89
+ #
90
+ # Returns nothing.
74
91
  def to_node(message)
75
92
  case message[TYPE]
76
93
  when STANZA then route_stanza(message)
@@ -80,6 +97,10 @@ module Vines
80
97
 
81
98
  # Send the stanza, from a remote cluster node, to locally connected
82
99
  # streams for the destination user.
100
+ #
101
+ # message - The parsed Hash of received message data.
102
+ #
103
+ # Returns nothing.
83
104
  def route_stanza(message)
84
105
  node = Nokogiri::XML(message[STANZA]).root rescue nil
85
106
  return unless node
@@ -95,6 +116,10 @@ module Vines
95
116
 
96
117
  # Update the roster information, that's cached in locally connected
97
118
  # streams, for this user.
119
+ #
120
+ # message - The parsed Hash of received message data.
121
+ #
122
+ # Returns nothing.
98
123
  def update_user(message)
99
124
  jid = JID.new(message['jid']).bare
100
125
  if user = @cluster.storage(jid.domain).find_user(jid)
data/lib/vines/config.rb CHANGED
@@ -21,6 +21,7 @@ module Vines
21
21
  end
22
22
 
23
23
  def initialize(&block)
24
+ @pepper = "" # no pepper set
24
25
  @certs = File.expand_path('conf/certs')
25
26
  @vhosts, @ports, @cluster = {}, {}, nil
26
27
  @null = Storage::Null.new
@@ -47,7 +48,7 @@ module Vines
47
48
  end
48
49
 
49
50
  def pepper(pepper=nil)
50
- pepper ? @pepper = pepper : @pepper = ""
51
+ pepper ? @pepper = pepper : @pepper
51
52
  end
52
53
 
53
54
  def domain_name
@@ -72,7 +73,20 @@ module Vines
72
73
  @cluster = Cluster.new(self, &block)
73
74
  end
74
75
 
75
- def log(level)
76
+ def log(file=nil, &block)
77
+ unless file.nil?
78
+ unless File.exists?(file)
79
+ File.new(file, 'w') rescue raise "log directory doesn't exists"
80
+ end
81
+
82
+ if File.exists?(file)
83
+ Vines::Log.set_log_file(file)
84
+ end
85
+ end
86
+ instance_eval(&block) if block
87
+ end
88
+
89
+ def level(level)
76
90
  const = Logger.const_get(level.to_s.upcase) rescue nil
77
91
  unless LOG_LEVELS.include?(level.to_s) && const
78
92
  raise "log level must be one of: #{LOG_LEVELS.join(', ')}"
data/lib/vines/log.rb CHANGED
@@ -20,5 +20,9 @@ module Vines
20
20
  end
21
21
  @@logger
22
22
  end
23
+
24
+ def self.set_log_file(file)
25
+ @@logger = Logger.new(file)
26
+ end
23
27
  end
24
28
  end
@@ -1,19 +1,39 @@
1
- require 'active_record'
1
+ # encoding: UTF-8
2
2
 
3
3
  module Vines
4
4
  class Storage
5
5
  class Sql < Storage
6
+ include Vines::Log
7
+
6
8
  register :sql
7
9
 
8
10
  class Person < ActiveRecord::Base; end
11
+ class Aspect < ActiveRecord::Base
12
+ belongs_to :users
13
+
14
+ has_many :aspect_memberships
15
+ has_many :contacts
16
+ end
17
+
18
+ class AspectMembership < ActiveRecord::Base
19
+ belongs_to :aspect
20
+ belongs_to :contact
21
+
22
+ has_one :users, :through => :contact
23
+ has_one :person, :through => :contact
24
+ end
25
+
9
26
  class Contact < ActiveRecord::Base
10
- belongs_to :user
27
+ belongs_to :users
11
28
  belongs_to :person
29
+
30
+ has_many :aspect_memberships
31
+ has_many :aspects, :through => :aspect_memberships
12
32
  end
13
33
 
14
34
  class User < ActiveRecord::Base
15
- has_many :contacts#, through: :user_id
16
- has_many :contact_people, :through => :contacts, :source => :person
35
+ has_many :contacts
36
+
17
37
  has_one :person, :foreign_key => :owner_id
18
38
  end
19
39
 
@@ -33,15 +53,15 @@ module Vines
33
53
  end
34
54
 
35
55
  def initialize(&block)
36
- raise "You configured diaspora-sql adapter without Diaspora" unless defined? AppConfig
37
- @config = {
38
- :adapter => AppConfig.adapter.to_s,
39
- :database => AppConfig.database.to_s,
40
- :host => AppConfig.host.to_s,
41
- :port => AppConfig.port.to_i,
42
- :username => AppConfig.username.to_s,
43
- :password => AppConfig.password.to_s
44
- }
56
+ @config = {}
57
+ unless defined? Rails
58
+ raise "You configured diaspora-sql adapter without Diaspora environment"
59
+ end
60
+
61
+ config = Rails.application.config.database_configuration[Rails.env]
62
+ %w[adapter database host port username password].each do |key|
63
+ @config[key.to_sym] = config[key]
64
+ end
45
65
 
46
66
  required = [:adapter, :database]
47
67
  required << [:host, :port] unless @config[:adapter] == 'sqlite3'
@@ -55,30 +75,16 @@ module Vines
55
75
  return if jid.empty?
56
76
  xuser = user_by_jid(jid)
57
77
  return Vines::User.new(jid: jid).tap do |user|
58
- user.name, user.password = xuser.username, xuser.authentication_token
78
+ user.name, user.password, user.token =
79
+ xuser.username,
80
+ xuser.encrypted_password,
81
+ xuser.authentication_token
59
82
 
60
83
  xuser.contacts.each do |contact|
61
- handle = contact.person.diaspora_handle
62
- ask = 'none'
63
- subscription = 'none'
64
-
65
- if contact.sharing && contact.receiving
66
- subscription = 'both'
67
- elsif contact.sharing && !contact.receiving
68
- ask = 'suscribe'
69
- subscription = 'from'
70
- elsif !contact.sharing && contact.receiving
71
- subscription = 'to'
72
- else
73
- ask = 'suscribe'
84
+ entry = build_roster_entry(contact)
85
+ unless entry.nil?
86
+ user.roster << entry
74
87
  end
75
- # finally build the roster entry
76
- user.roster << Vines::Contact.new(
77
- jid: handle,
78
- name: handle.gsub(/\@.*?$/, ''),
79
- subscription: subscription,
80
- ask: ask
81
- ) if handle
82
88
  end
83
89
  end if xuser
84
90
  end
@@ -87,17 +93,18 @@ module Vines
87
93
  def authenticate(username, password)
88
94
  user = find_user(username)
89
95
 
96
+ pepper = password
97
+ pepper << Config.instance.pepper unless Config
90
98
  dbhash = BCrypt::Password.new(user.password) rescue nil
91
- hash = BCrypt::Engine.hash_secret("#{password}#{Config.instance.pepper}", dbhash.salt) rescue nil
99
+ hash = BCrypt::Engine.hash_secret(pepper, dbhash.salt) rescue nil
92
100
 
93
101
  userAuth = ((hash && dbhash) && hash == dbhash)
94
- tokenAuth = ((password && user.password) && password == user.password)
102
+ tokenAuth = ((password && user) && password == user.token)
95
103
  (tokenAuth || userAuth)? user : nil
96
104
  end
97
105
 
98
106
  def save_user(user)
99
107
  # do nothing
100
- #log.error("You cannot save a user via XMPP server!")
101
108
  end
102
109
  with_connection :save_user
103
110
 
@@ -125,7 +132,7 @@ module Vines
125
132
 
126
133
  private
127
134
  def establish_connection
128
- ActiveRecord::Base.logger = Logger.new('/dev/null')
135
+ ActiveRecord::Base.logger = log # using vines logger
129
136
  ActiveRecord::Base.establish_connection(@config)
130
137
  end
131
138
 
@@ -133,6 +140,37 @@ module Vines
133
140
  name = JID.new(jid).node
134
141
  Sql::User.find_by_username(name)
135
142
  end
143
+
144
+ def build_roster_entry(contact)
145
+ groups = Array.new
146
+ contact.aspects.each do |aspect|
147
+ groups.push(aspect.name)
148
+ end
149
+
150
+ handle = contact.person.diaspora_handle
151
+ ask = 'none'
152
+ subscription = 'none'
153
+
154
+ if contact.sharing && contact.receiving
155
+ subscription = 'both'
156
+ elsif contact.sharing && !contact.receiving
157
+ ask = 'suscribe'
158
+ subscription = 'from'
159
+ elsif !contact.sharing && contact.receiving
160
+ subscription = 'to'
161
+ else
162
+ ask = 'suscribe'
163
+ end
164
+
165
+ # finally build the roster entry
166
+ return Vines::Contact.new(
167
+ jid: handle,
168
+ name: handle.gsub(/\@.*?$/, ''),
169
+ subscription: subscription,
170
+ groups: groups,
171
+ ask: ask
172
+ ) || nil
173
+ end
136
174
  end
137
175
  end
138
176
  end
data/lib/vines/store.rb CHANGED
@@ -1,37 +1,33 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Vines
4
-
5
4
  # An X509 certificate store that validates certificate trust chains.
6
5
  # This uses the conf/certs/*.crt files as the list of trusted root
7
6
  # CA certificates.
8
7
  class Store
9
- include Vines::Log
10
-
11
8
  @@sources = nil
12
9
 
13
10
  # Create a certificate store to read certificate files from the given
14
11
  # directory.
12
+ #
13
+ # dir - The String directory name (absolute or relative).
15
14
  def initialize(dir)
16
15
  @dir = File.expand_path(dir)
17
16
  @store = OpenSSL::X509::Store.new
18
- certs.each {|c|
19
- begin
20
- @store.add_cert(c)
21
- rescue
22
- # do nothing cert is already known
23
- log.warn("WARNING! There are duplicate certificates")
24
- end
25
- }
17
+ certs.each {|cert| append(cert) }
26
18
  end
27
19
 
28
20
  # Return true if the certificate is signed by a CA certificate in the
29
21
  # store. If the certificate can be trusted, it's added to the store so
30
22
  # it can be used to trust other certs.
23
+ #
24
+ # pem - The PEM encoded certificate String.
25
+ #
26
+ # Returns true if the certificate is trusted.
31
27
  def trusted?(pem)
32
28
  if cert = OpenSSL::X509::Certificate.new(pem) rescue nil
33
29
  @store.verify(cert).tap do |trusted|
34
- @store.add_cert(cert) if trusted rescue nil
30
+ append(cert) if trusted
35
31
  end
36
32
  end
37
33
  end
@@ -39,6 +35,11 @@ module Vines
39
35
  # Return true if the domain name matches one of the names in the
40
36
  # certificate. In other words, is the certificate provided to us really
41
37
  # for the domain to which we think we're connected?
38
+ #
39
+ # pem - The PEM encoded certificate String.
40
+ # domain - The domain name String.
41
+ #
42
+ # Returns true if the certificate was issued for the domain.
42
43
  def domain?(pem, domain)
43
44
  if cert = OpenSSL::X509::Certificate.new(pem) rescue nil
44
45
  OpenSSL::SSL.verify_certificate_identity(cert, domain) rescue false
@@ -48,8 +49,10 @@ module Vines
48
49
  # Return the trusted root CA certificates installed in conf/certs. These
49
50
  # certificates are used to start the trust chain needed to validate certs
50
51
  # we receive from clients and servers.
52
+ #
53
+ # Returns an Array of OpenSSL::X509::Certificate objects.
51
54
  def certs
52
- unless @@sources
55
+ @@sources ||= begin
53
56
  pattern = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
54
57
  files = Dir[File.join(@dir, '*.crt')]
55
58
  files << AppConfig.environment.certificate_authorities if defined?(AppConfig)
@@ -61,7 +64,7 @@ module Vines
61
64
  [name, certs]
62
65
  end
63
66
  end
64
- @@sources = Hash[pairs]
67
+ Hash[pairs]
65
68
  end
66
69
  @@sources.values.flatten
67
70
  end
@@ -71,31 +74,43 @@ module Vines
71
74
  # wildcard certificate files to serve several subdomains.
72
75
  #
73
76
  # Finding the certificate and private key file for a domain follows these steps:
74
- # - look for <domain>.crt and <domain>.key files in the conf/certs directory.
75
- # if found, return those file names, else
76
- # - inspect all conf/certs/*.crt files for certificates that contain the
77
+ #
78
+ # - Look for <domain>.crt and <domain>.key files in the conf/certs
79
+ # directory. If found, return those file names, otherwise . . .
80
+ #
81
+ # - Inspect all conf/certs/*.crt files for certificates that contain the
77
82
  # domain name either as the subject common name (CN) or as a DNS
78
83
  # subjectAltName. The corresponding private key must be in a file of the
79
84
  # same name as the certificate's, but with a .key extension.
80
85
  #
81
- # So in the simplest configuration, the tea.wonderland.lit encryption files would
82
- # be named conf/certs/tea.wonderland.lit.crt and conf/certs/tea.wonderland.lit.key.
86
+ # So in the simplest configuration, the tea.wonderland.lit encryption files
87
+ # would be named:
88
+ #
89
+ # - conf/certs/tea.wonderland.lit.crt
90
+ # - conf/certs/tea.wonderland.lit.key
83
91
  #
84
- # However, in the case of a wildcard certificate for *.wonderland.lit, the
85
- # files would be conf/certs/wonderland.lit.crt and conf/certs/wonderland.lit.key.
86
- # These same two files would be returned for the subdomains of tea.wonderland.lit,
87
- # crumpets.wonderland.lit, etc.
92
+ # However, in the case of a wildcard certificate for *.wonderland.lit,
93
+ # the files would be:
94
+ #
95
+ # - conf/certs/wonderland.lit.crt
96
+ # - conf/certs/wonderland.lit.key
97
+ #
98
+ # These same two files would be returned for the subdomains of:
99
+ #
100
+ # - tea.wonderland.lit
101
+ # - crumpets.wonderland.lit
102
+ # - etc.
103
+ #
104
+ # domain - The String domain name.
105
+ #
106
+ # Returns a two element String array for the certificate and private key
107
+ # file names or nil if not found.
88
108
  def files_for_domain(domain)
89
109
  crt = File.expand_path("#{domain}.crt", @dir)
90
110
  key = File.expand_path("#{domain}.key", @dir)
91
- # diaspora keys will be prioritized
92
- if defined?(AppConfig)
93
- crt = AppConfig.server.chat.certificate
94
- key = AppConfig.server.chat.key
95
- end
96
111
  return [crt, key] if File.exists?(crt) && File.exists?(key)
97
112
 
98
- # might be a wildcard cert file
113
+ # Might be a wildcard cert file.
99
114
  @@sources.each do |file, certs|
100
115
  certs.each do |cert|
101
116
  if OpenSSL::SSL.verify_certificate_identity(cert, domain)
@@ -106,5 +121,19 @@ module Vines
106
121
  end
107
122
  nil
108
123
  end
124
+
125
+ private
126
+
127
+ # Add a trusted certificate to the store, suppressing any OpenSSL errors
128
+ # caused by the certificate already being stored.
129
+ #
130
+ # cert - The OpenSSL::X509::Certificate to add.
131
+ #
132
+ # Returns nothing.
133
+ def append(cert)
134
+ @store.add_cert(cert)
135
+ rescue OpenSSL::X509::StoreError
136
+ # Already added to store.
137
+ end
109
138
  end
110
139
  end
data/lib/vines/user.rb CHANGED
@@ -4,7 +4,7 @@ module Vines
4
4
  class User
5
5
  include Comparable
6
6
 
7
- attr_accessor :name, :password, :roster
7
+ attr_accessor :name, :token, :password, :roster
8
8
  attr_reader :jid
9
9
 
10
10
  def initialize(args={})
@@ -13,6 +13,7 @@ module Vines
13
13
 
14
14
  @name = args[:name]
15
15
  @password = args[:password]
16
+ @token = args[:token]
16
17
  @roster = args[:roster] || []
17
18
  end
18
19
 
@@ -30,6 +31,7 @@ module Vines
30
31
  def update_from(user)
31
32
  @name = user.name
32
33
  @password = user.password
34
+ @token = user.token
33
35
  @roster = user.roster.map {|c| c.clone }
34
36
  end
35
37
 
data/lib/vines/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Vines
4
4
  # vines forked version 0.4.9
5
- VERSION = '0.1.2'
5
+ VERSION = '0.1.21'
6
6
  end
data/lib/vines.rb CHANGED
@@ -55,13 +55,13 @@ module Vines
55
55
  end
56
56
 
57
57
  begin
58
- # try to initialize diaspora AppConfig
58
+ # try to initialize diaspora configuration
59
+ require "#{Dir.pwd}/config/application.rb"
59
60
  require "#{Dir.pwd}/config/load_config.rb"
60
- rescue LoadError
61
- puts "Cannot find Diaspora environment! Fallback to vines configuration."
62
- end
61
+ rescue LoadError; end
63
62
 
64
63
  %w[
64
+ active_record
65
65
  base64
66
66
  bcrypt
67
67
  digest/sha1
data/test/config_test.rb CHANGED
@@ -353,10 +353,23 @@ describe Vines::Config do
353
353
  assert_equal 1, config.ports.size
354
354
  end
355
355
 
356
+ def test_not_existing_file_path
357
+ assert_raises(RuntimeError) do
358
+ config = Vines::Config.new do
359
+ log 'not/existing/path.log'
360
+ end
361
+ host 'wonderland.lit' do
362
+ storage(:fs) { dir Dir.tmpdir }
363
+ end
364
+ end
365
+ end
366
+
356
367
  def test_invalid_log_level
357
368
  assert_raises(RuntimeError) do
358
369
  config = Vines::Config.new do
359
- log 'bogus'
370
+ log 'vines.log' do
371
+ level 'bogus'
372
+ end
360
373
  host 'wonderland.lit' do
361
374
  storage(:fs) { dir Dir.tmpdir }
362
375
  end
@@ -366,7 +379,9 @@ describe Vines::Config do
366
379
 
367
380
  def test_valid_log_level
368
381
  config = Vines::Config.new do
369
- log :error
382
+ log 'vines.log' do
383
+ level :error
384
+ end
370
385
  host 'wonderland.lit' do
371
386
  storage(:fs) { dir Dir.tmpdir }
372
387
  end
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+ require 'storage/sql_schema'
5
+
6
+ module Diaspora
7
+ class Application < Rails::Application
8
+ def config.database_configuration
9
+ {
10
+ "development" => {
11
+ "adapter" => "sqlite3",
12
+ "database" => "test.db"
13
+ }
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ describe Vines::Storage::Sql do
20
+ include SqlSchema
21
+
22
+ before do
23
+ storage && create_schema(:force => true)
24
+
25
+ Vines::Storage::Sql::User.new(
26
+ username: "test",
27
+ email: "test@test.de",
28
+ encrypted_password: "$2a$10$c2G6rHjGeamQIOFI0c1/b.4mvFBw4AfOtgVrAkO1QPMuAyporj5e6", # pppppp
29
+ authentication_token: "1234"
30
+ ).save
31
+ end
32
+
33
+ after do
34
+ db = Rails.application.config.database_configuration["development"]["database"]
35
+ File.delete(db) if File.exist?(db)
36
+ end
37
+
38
+ def test_find_user
39
+ fibered do
40
+ db = storage
41
+ user = db.find_user(nil)
42
+ assert_nil user
43
+
44
+ user = db.find_user("test@local.host")
45
+ assert (user != nil), "no user found"
46
+ assert_equal "test", user.name
47
+
48
+ user = db.find_user(Vines::JID.new("test@local.host"))
49
+ assert (user != nil), "no user found"
50
+ assert_equal "test", user.name
51
+
52
+ user = db.find_user(Vines::JID.new("test@local.host/resource"))
53
+ assert (user != nil), "no user found"
54
+ assert_equal "test", user.name
55
+ end
56
+ end
57
+
58
+ def test_authenticate
59
+ fibered do
60
+ db = storage
61
+
62
+ assert_nil db.authenticate(nil, nil)
63
+ assert_nil db.authenticate(nil, "secret")
64
+ assert_nil db.authenticate("bogus", nil)
65
+
66
+ # user credential auth
67
+ pepper = "065eb8798b181ff0ea2c5c16aee0ff8b70e04e2ee6bd6e08b49da46924223e39127d5335e466207d42bf2a045c12be5f90e92012a4f05f7fc6d9f3c875f4c95b"
68
+ user = db.authenticate("test@test.de", "pppppp#{pepper}")
69
+ assert (user != nil), "no user found"
70
+ assert_equal "test", user.name
71
+
72
+ # user token auth
73
+ user = db.authenticate("test@test.de", "1234")
74
+ assert (user != nil), "no user found"
75
+ assert_equal "test", user.name
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,117 @@
1
+ module SqlSchema
2
+ def fibered
3
+ EM.run do
4
+ Fiber.new do
5
+ yield
6
+ EM.stop
7
+ end.resume
8
+ end
9
+ end
10
+
11
+ def storage
12
+ Vines::Storage::Sql.new
13
+ end
14
+
15
+ def create_schema(args={})
16
+ args[:force] ||= false
17
+
18
+ ActiveRecord::Schema.define do
19
+ create_table "people", :force => true do |t|
20
+ t.string "guid", :null => false
21
+ t.text "url", :null => false
22
+ t.string "diaspora_handle", :null => false
23
+ t.text "serialized_public_key", :null => false
24
+ t.integer "owner_id"
25
+ t.datetime "created_at", :null => false
26
+ t.datetime "updated_at", :null => false
27
+ t.boolean "closed_account", :default => false
28
+ t.integer "fetch_status", :default => 0
29
+ end
30
+
31
+ add_index "people", ["diaspora_handle"], :name => "index_people_on_diaspora_handle", :unique => true
32
+ add_index "people", ["guid"], :name => "index_people_on_guid", :unique => true
33
+ add_index "people", ["owner_id"], :name => "index_people_on_owner_id", :unique => true
34
+
35
+ create_table "aspects", :force => true do |t|
36
+ t.string "name", :null => false
37
+ t.integer "user_id", :null => false
38
+ t.datetime "created_at", :null => false
39
+ t.datetime "updated_at", :null => false
40
+ t.boolean "contacts_visible", :default => true, :null => false
41
+ t.integer "order_id"
42
+ end
43
+
44
+ add_index "aspects", ["user_id", "contacts_visible"], :name => "index_aspects_on_user_id_and_contacts_visible"
45
+ add_index "aspects", ["user_id"], :name => "index_aspects_on_user_id"
46
+
47
+ create_table "aspect_memberships", :force => true do |t|
48
+ t.integer "aspect_id", :null => false
49
+ t.integer "contact_id", :null => false
50
+ t.datetime "created_at", :null => false
51
+ t.datetime "updated_at", :null => false
52
+ end
53
+
54
+ add_index "aspect_memberships", ["aspect_id", "contact_id"], :name => "index_aspect_memberships_on_aspect_id_and_contact_id", :unique => true
55
+ add_index "aspect_memberships", ["aspect_id"], :name => "index_aspect_memberships_on_aspect_id"
56
+ add_index "aspect_memberships", ["contact_id"], :name => "index_aspect_memberships_on_contact_id"
57
+
58
+ create_table "contacts", :force => true do |t|
59
+ t.integer "user_id", :null => false
60
+ t.integer "person_id", :null => false
61
+ t.datetime "created_at", :null => false
62
+ t.datetime "updated_at", :null => false
63
+ t.boolean "sharing", :default => false, :null => false
64
+ t.boolean "receiving", :default => false, :null => false
65
+ end
66
+
67
+ add_index "contacts", ["person_id"], :name => "index_contacts_on_person_id"
68
+ add_index "contacts", ["user_id", "person_id"], :name => "index_contacts_on_user_id_and_person_id", :unique => true
69
+
70
+ create_table "users", :force => true do |t|
71
+ t.string "username"
72
+ t.text "serialized_private_key"
73
+ t.boolean "getting_started", :default => true, :null => false
74
+ t.boolean "disable_mail", :default => false, :null => false
75
+ t.string "language"
76
+ t.string "email", :default => "", :null => false
77
+ t.string "encrypted_password", :default => "", :null => false
78
+ t.string "invitation_token", :limit => 60
79
+ t.datetime "invitation_sent_at"
80
+ t.string "reset_password_token"
81
+ t.datetime "remember_created_at"
82
+ t.integer "sign_in_count", :default => 0
83
+ t.datetime "current_sign_in_at"
84
+ t.datetime "last_sign_in_at"
85
+ t.string "current_sign_in_ip"
86
+ t.string "last_sign_in_ip"
87
+ t.datetime "created_at", :null => false
88
+ t.datetime "updated_at", :null => false
89
+ t.string "invitation_service", :limit => 127
90
+ t.string "invitation_identifier", :limit => 127
91
+ t.integer "invitation_limit"
92
+ t.integer "invited_by_id"
93
+ t.string "invited_by_type"
94
+ t.string "authentication_token", :limit => 30
95
+ t.string "unconfirmed_email"
96
+ t.string "confirm_email_token", :limit => 30
97
+ t.datetime "locked_at"
98
+ t.boolean "show_community_spotlight_in_stream", :default => true, :null => false
99
+ t.boolean "auto_follow_back", :default => false
100
+ t.integer "auto_follow_back_aspect_id"
101
+ t.text "hidden_shareables"
102
+ t.datetime "reset_password_sent_at"
103
+ t.datetime "last_seen"
104
+ end
105
+
106
+ add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
107
+ add_index "users", ["email"], :name => "index_users_on_email"
108
+ add_index "users", ["invitation_service", "invitation_identifier"], :name => "index_users_on_invitation_service_and_invitation_identifier", :unique => true
109
+ add_index "users", ["invitation_token"], :name => "index_users_on_invitation_token"
110
+ add_index "users", ["username"], :name => "index_users_on_username", :unique => true
111
+
112
+ #add_foreign_key "aspect_memberships", "aspects", name: "aspect_memberships_aspect_id_fk", dependent: :delete
113
+ #add_foreign_key "aspect_memberships", "contacts", name: "aspect_memberships_contact_id_fk", dependent: :delete
114
+ #add_foreign_key "contacts", "people", name: "contacts_person_id_fk", dependent: :delete
115
+ end
116
+ end
117
+ end
data/test/store_test.rb CHANGED
@@ -3,96 +3,113 @@
3
3
  require 'test_helper'
4
4
 
5
5
  describe Vines::Store do
6
- before do
7
- dir = 'conf/certs'
8
-
9
- domain, key = certificate('wonderland.lit')
10
- File.open("#{dir}/wonderland.lit.crt", 'w') {|f| f.write(domain) }
11
- File.open("#{dir}/wonderland.lit.key", 'w') {|f| f.write(key) }
12
-
13
- wildcard, key = certificate('*.wonderland.lit')
14
- File.open("#{dir}/wildcard.lit.crt", 'w') {|f| f.write(wildcard) }
15
- File.open("#{dir}/wildcard.lit.key", 'w') {|f| f.write(key) }
6
+ let(:dir) { 'conf/certs' }
7
+ let(:domain_pair) { certificate('wonderland.lit') }
8
+ let(:wildcard_pair) { certificate('*.wonderland.lit') }
9
+ subject { Vines::Store.new(dir) }
16
10
 
17
- @store = Vines::Store.new('conf/certs')
11
+ before do
12
+ @files =
13
+ save('wonderland.lit', domain_pair) +
14
+ save('wildcard.lit', wildcard_pair) +
15
+ save('duplicate.lit', domain_pair)
18
16
  end
19
17
 
20
18
  after do
21
- %w[wonderland.lit.crt wonderland.lit.key wildcard.lit.crt wildcard.lit.key].each do |f|
22
- name = "conf/certs/#{f}"
19
+ @files.each do |name|
23
20
  File.delete(name) if File.exists?(name)
24
21
  end
25
22
  end
26
23
 
27
- it 'parses certificate files' do
28
- refute @store.certs.empty?
29
- assert_equal OpenSSL::X509::Certificate, @store.certs.first.class
30
- end
24
+ describe 'creating a store' do
25
+ it 'parses certificate files' do
26
+ refute subject.certs.empty?
27
+ assert_equal OpenSSL::X509::Certificate, subject.certs.first.class
28
+ end
29
+
30
+ it 'ignores expired certificates' do
31
+ assert subject.certs.all? {|c| c.not_after > Time.new }
32
+ end
31
33
 
32
- it 'ignores expired certificates' do
33
- assert @store.certs.all? {|c| c.not_after > Time.new }
34
+ it 'does not raise an error for duplicate certificates' do
35
+ assert Vines::Store.new(dir)
36
+ end
34
37
  end
35
38
 
36
39
  describe 'files_for_domain' do
37
40
  it 'handles invalid input' do
38
- assert_nil @store.files_for_domain(nil)
39
- assert_nil @store.files_for_domain('')
41
+ assert_nil subject.files_for_domain(nil)
42
+ assert_nil subject.files_for_domain('')
40
43
  end
41
44
 
42
45
  it 'finds files by name' do
43
- refute_nil @store.files_for_domain('wonderland.lit')
44
- cert, key = @store.files_for_domain('wonderland.lit')
46
+ refute_nil subject.files_for_domain('wonderland.lit')
47
+ cert, key = subject.files_for_domain('wonderland.lit')
45
48
  assert_certificate_matches_key cert, key
46
49
  assert_equal 'wonderland.lit.crt', File.basename(cert)
47
50
  assert_equal 'wonderland.lit.key', File.basename(key)
48
51
  end
49
52
 
50
53
  it 'finds files for wildcard' do
51
- refute_nil @store.files_for_domain('foo.wonderland.lit')
52
- cert, key = @store.files_for_domain('foo.wonderland.lit')
54
+ refute_nil subject.files_for_domain('foo.wonderland.lit')
55
+ cert, key = subject.files_for_domain('foo.wonderland.lit')
53
56
  assert_certificate_matches_key cert, key
54
57
  assert_equal 'wildcard.lit.crt', File.basename(cert)
55
58
  assert_equal 'wildcard.lit.key', File.basename(key)
56
59
  end
57
60
  end
58
61
 
62
+ describe 'trusted?' do
63
+ it 'does not trust malformed certificates' do
64
+ refute subject.trusted?('bogus')
65
+ end
66
+
67
+ it 'does not trust unsigned certificates' do
68
+ pair = certificate('something.lit')
69
+ refute subject.trusted?(pair.cert)
70
+ end
71
+ end
72
+
59
73
  describe 'domain?' do
60
74
  it 'handles invalid input' do
61
- cert, key = certificate('wonderland.lit')
62
- refute @store.domain?(nil, nil)
63
- refute @store.domain?(cert, nil)
64
- refute @store.domain?(cert, '')
65
- refute @store.domain?(nil, '')
66
- assert @store.domain?(cert, 'wonderland.lit')
75
+ pair = certificate('wonderland.lit')
76
+ refute subject.domain?(nil, nil)
77
+ refute subject.domain?(pair.cert, nil)
78
+ refute subject.domain?(pair.cert, '')
79
+ refute subject.domain?(nil, '')
80
+ assert subject.domain?(pair.cert, 'wonderland.lit')
67
81
  end
68
82
 
69
83
  it 'verifies certificate subject domains' do
70
- cert, key = certificate('wonderland.lit')
71
- refute @store.domain?(cert, 'bogus')
72
- refute @store.domain?(cert, 'www.wonderland.lit')
73
- assert @store.domain?(cert, 'wonderland.lit')
84
+ pair = certificate('wonderland.lit')
85
+ refute subject.domain?(pair.cert, 'bogus')
86
+ refute subject.domain?(pair.cert, 'www.wonderland.lit')
87
+ assert subject.domain?(pair.cert, 'wonderland.lit')
74
88
  end
75
89
 
76
90
  it 'verifies certificate subject alt domains' do
77
- cert, key = certificate('wonderland.lit', 'www.wonderland.lit')
78
- refute @store.domain?(cert, 'bogus')
79
- refute @store.domain?(cert, 'tea.wonderland.lit')
80
- assert @store.domain?(cert, 'www.wonderland.lit')
81
- assert @store.domain?(cert, 'wonderland.lit')
91
+ pair = certificate('wonderland.lit', 'www.wonderland.lit')
92
+ refute subject.domain?(pair.cert, 'bogus')
93
+ refute subject.domain?(pair.cert, 'tea.wonderland.lit')
94
+ assert subject.domain?(pair.cert, 'www.wonderland.lit')
95
+ assert subject.domain?(pair.cert, 'wonderland.lit')
82
96
  end
83
97
 
84
98
  it 'verifies certificate wildcard domains' do
85
- cert, key = certificate('wonderland.lit', '*.wonderland.lit')
86
- refute @store.domain?(cert, 'bogus')
87
- refute @store.domain?(cert, 'one.two.wonderland.lit')
88
- assert @store.domain?(cert, 'tea.wonderland.lit')
89
- assert @store.domain?(cert, 'www.wonderland.lit')
90
- assert @store.domain?(cert, 'wonderland.lit')
99
+ pair = certificate('wonderland.lit', '*.wonderland.lit')
100
+ refute subject.domain?(pair.cert, 'bogus')
101
+ refute subject.domain?(pair.cert, 'one.two.wonderland.lit')
102
+ assert subject.domain?(pair.cert, 'tea.wonderland.lit')
103
+ assert subject.domain?(pair.cert, 'www.wonderland.lit')
104
+ assert subject.domain?(pair.cert, 'wonderland.lit')
91
105
  end
92
106
  end
93
107
 
94
108
  private
95
109
 
110
+ # A public certificate + private key pair.
111
+ Pair = Struct.new(:cert, :key)
112
+
96
113
  def assert_certificate_matches_key(cert, key)
97
114
  refute_nil cert
98
115
  refute_nil key
@@ -102,7 +119,7 @@ describe Vines::Store do
102
119
  end
103
120
 
104
121
  def certificate(domain, altname=nil)
105
- # use small key so tests are fast
122
+ # Use small key so tests are fast.
106
123
  key = OpenSSL::PKey::RSA.generate(256)
107
124
 
108
125
  name = OpenSSL::X509::Name.parse("/C=US/ST=Colorado/L=Denver/O=Test/CN=#{domain}")
@@ -125,6 +142,21 @@ describe Vines::Store do
125
142
  ].map {|k, v| factory.create_ext(k, v) }
126
143
  end
127
144
 
128
- [cert.to_pem, key.to_pem]
145
+ Pair.new(cert.to_pem, key.to_pem)
146
+ end
147
+
148
+ # Write the domain's certificate and private key files to the filesystem for
149
+ # the store to use.
150
+ #
151
+ # domain - The domain name String to use in the file name (e.g. wonderland.lit).
152
+ # pair - The Pair containing the public certificate and private key data.
153
+ #
154
+ # Returns a String Array of file names that were written.
155
+ def save(domain, pair)
156
+ crt = File.expand_path("#{domain}.crt", dir)
157
+ key = File.expand_path("#{domain}.key", dir)
158
+ File.open(crt, 'w') {|f| f.write(pair.cert) }
159
+ File.open(key, 'w') {|f| f.write(pair.key) }
160
+ [crt, key]
129
161
  end
130
162
  end
data/test/test_helper.rb CHANGED
@@ -4,6 +4,7 @@ require 'tmpdir'
4
4
  require 'vines'
5
5
  require 'ext/nokogiri'
6
6
  require 'minitest/autorun'
7
+ require 'rails/all'
7
8
 
8
9
  class MiniTest::Spec
9
10
 
data/vines.gemspec CHANGED
@@ -18,11 +18,15 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_dependency 'bcrypt', '~> 3.1'
20
20
  s.add_dependency 'em-hiredis', '~> 0.1.1'
21
- s.add_dependency 'eventmachine', '~> 1.0.3'
22
- s.add_dependency 'http_parser.rb', '~> 0.5.3'
23
- s.add_dependency 'net-ldap', '~> 0.3.1'
24
- s.add_dependency 'nokogiri', '>= 1.5.10'
21
+ s.add_dependency 'eventmachine', '~> 1.0'
22
+ s.add_dependency 'http_parser.rb', '~> 0.6'
23
+ s.add_dependency 'net-ldap', '~> 0.6'
24
+ s.add_dependency 'nokogiri', '~> 1.6'
25
+ s.add_dependency 'activerecord', '~> 4.1.4'
25
26
 
27
+
28
+ s.add_development_dependency 'rails', '~> 4.1.4'
29
+ s.add_development_dependency 'sqlite3', '~> 1.3.9'
26
30
  s.add_development_dependency 'minitest', '~> 5.3'
27
31
  s.add_development_dependency 'rake', '~> 10.3'
28
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diaspora-vines
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Graham
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-29 00:00:00.000000000 Z
12
+ date: 2014-09-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bcrypt
@@ -45,56 +45,98 @@ dependencies:
45
45
  requirements:
46
46
  - - ~>
47
47
  - !ruby/object:Gem::Version
48
- version: 1.0.3
48
+ version: '1.0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - ~>
54
54
  - !ruby/object:Gem::Version
55
- version: 1.0.3
55
+ version: '1.0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: http_parser.rb
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - ~>
61
61
  - !ruby/object:Gem::Version
62
- version: 0.5.3
62
+ version: '0.6'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - ~>
68
68
  - !ruby/object:Gem::Version
69
- version: 0.5.3
69
+ version: '0.6'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: net-ldap
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ~>
75
75
  - !ruby/object:Gem::Version
76
- version: 0.3.1
76
+ version: '0.6'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - ~>
82
82
  - !ruby/object:Gem::Version
83
- version: 0.3.1
83
+ version: '0.6'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: nokogiri
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - '>='
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: '1.6'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: '1.6'
98
+ - !ruby/object:Gem::Dependency
99
+ name: activerecord
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ~>
89
103
  - !ruby/object:Gem::Version
90
- version: 1.5.10
104
+ version: 4.1.4
91
105
  type: :runtime
92
106
  prerelease: false
93
107
  version_requirements: !ruby/object:Gem::Requirement
94
108
  requirements:
95
- - - '>='
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: 4.1.4
112
+ - !ruby/object:Gem::Dependency
113
+ name: rails
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ~>
117
+ - !ruby/object:Gem::Version
118
+ version: 4.1.4
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 4.1.4
126
+ - !ruby/object:Gem::Dependency
127
+ name: sqlite3
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ~>
131
+ - !ruby/object:Gem::Version
132
+ version: 1.3.9
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ~>
96
138
  - !ruby/object:Gem::Version
97
- version: 1.5.10
139
+ version: 1.3.9
98
140
  - !ruby/object:Gem::Dependency
99
141
  name: minitest
100
142
  requirement: !ruby/object:Gem::Requirement
@@ -282,6 +324,8 @@ files:
282
324
  - test/storage/local_test.rb
283
325
  - test/storage/mock_redis.rb
284
326
  - test/storage/null_test.rb
327
+ - test/storage/sql.rb
328
+ - test/storage/sql_schema.rb
285
329
  - test/storage/storage_tests.rb
286
330
  - test/storage_test.rb
287
331
  - test/store_test.rb
@@ -325,7 +369,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
325
369
  version: '0'
326
370
  requirements: []
327
371
  rubyforge_project:
328
- rubygems_version: 2.3.0
372
+ rubygems_version: 2.4.1
329
373
  signing_key:
330
374
  specification_version: 4
331
375
  summary: Diaspora-vines is a Vines fork build for diaspora integration.
@@ -381,7 +425,9 @@ test_files:
381
425
  - test/config_test.rb
382
426
  - test/test_helper.rb
383
427
  - test/storage/storage_tests.rb
428
+ - test/storage/sql_schema.rb
384
429
  - test/storage/null_test.rb
385
430
  - test/storage/mock_redis.rb
386
431
  - test/storage/ldap_test.rb
387
432
  - test/storage/local_test.rb
433
+ - test/storage/sql.rb