vines-services 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -5
- data/Rakefile +13 -11
- data/bin/vines-services +0 -1
- data/lib/vines/services/command/init.rb +156 -139
- data/lib/vines/services/controller/messages_controller.rb +8 -3
- data/lib/vines/services/controller/users_controller.rb +11 -11
- data/lib/vines/services/indexer.rb +1 -1
- data/lib/vines/services/priority_queue.rb +12 -12
- data/lib/vines/services/roster.rb +85 -46
- data/lib/vines/services/storage/couchdb/service.rb +1 -1
- data/lib/vines/services/storage/couchdb/system.rb +1 -1
- data/lib/vines/services/storage/couchdb/upload.rb +1 -1
- data/lib/vines/services/storage/couchdb/user.rb +13 -3
- data/lib/vines/services/storage/couchdb.rb +8 -12
- data/lib/vines/services/version.rb +1 -1
- data/test/priority_queue_test.rb +1 -1
- data/web/coffeescripts/api.coffee +6 -0
- data/web/coffeescripts/files.coffee +65 -39
- data/web/coffeescripts/services.coffee +5 -2
- data/web/coffeescripts/setup.coffee +35 -12
- data/web/coffeescripts/systems.coffee +115 -228
- data/web/javascripts/api.js +69 -0
- data/web/javascripts/app.js +2 -0
- data/web/javascripts/commands.js +28 -0
- data/web/javascripts/files.js +424 -0
- data/web/javascripts/init.js +27 -0
- data/web/javascripts/services.js +404 -0
- data/web/javascripts/setup.js +485 -0
- data/web/javascripts/systems.js +342 -0
- data/web/stylesheets/app.css +714 -0
- data/web/stylesheets/services.css +4 -14
- data/web/stylesheets/setup.css +1 -2
- data/web/stylesheets/systems.css +124 -108
- metadata +95 -92
data/README
CHANGED
@@ -11,6 +11,8 @@ defined as: platform is 'mac_os_x' and platform_version starts with '10.7'.
|
|
11
11
|
As machines update to Lion, they will join this service automatically and may
|
12
12
|
be managed as one group.
|
13
13
|
|
14
|
+
Additional documentation can be found at www.getvines.com.
|
15
|
+
|
14
16
|
== Usage
|
15
17
|
|
16
18
|
1. gem install vines-services
|
@@ -19,11 +21,10 @@ be managed as one group.
|
|
19
21
|
|
20
22
|
== Dependencies
|
21
23
|
|
22
|
-
* bcrypt-ruby >= 3.0.
|
23
|
-
* blather >= 0.5.
|
24
|
+
* bcrypt-ruby >= 3.0.1
|
25
|
+
* blather >= 0.5.8
|
24
26
|
* citrus >= 2.4.0
|
25
27
|
* eventmachine >= 0.12.10
|
26
|
-
* nokogiri >= 1.4.4
|
27
28
|
* sqlite3 >= 1.3.4
|
28
29
|
* ruby >= 1.9.2
|
29
30
|
|
@@ -32,8 +33,7 @@ $ sudo apt-get install build-essential ruby1.9.1 ruby1.9.1-dev libxml2-dev libxs
|
|
32
33
|
|
33
34
|
== Contact
|
34
35
|
|
35
|
-
* David Graham
|
36
|
-
* Chris Johnson <chris@negativecode.com>
|
36
|
+
* David Graham <david@negativecode.com>
|
37
37
|
|
38
38
|
== License
|
39
39
|
|
data/Rakefile
CHANGED
@@ -5,10 +5,11 @@ require 'rubygems/package_task'
|
|
5
5
|
require 'nokogiri'
|
6
6
|
require_relative 'lib/vines/services/version'
|
7
7
|
|
8
|
+
CLOBBER.include('pkg', 'web/javascripts', 'web/stylesheets/app.css')
|
9
|
+
|
8
10
|
spec = Gem::Specification.new do |s|
|
9
11
|
s.name = "vines-services"
|
10
12
|
s.version = Vines::Services::VERSION
|
11
|
-
s.date = Time.now.strftime("%Y-%m-%d")
|
12
13
|
|
13
14
|
s.summary = "An XMPP component that broadcasts shell commands to many agents."
|
14
15
|
s.description = "Vines Services are dynamically updated groups of systems based
|
@@ -16,17 +17,16 @@ on criteria like hostname, installed software, operating system, etc. Send a
|
|
16
17
|
command to the service and it runs on every system in the group. Services, files
|
17
18
|
and permissions are managed via the bundled web application."
|
18
19
|
|
19
|
-
s.authors = ["David Graham"
|
20
|
-
s.email = %w[david@negativecode.com
|
20
|
+
s.authors = ["David Graham"]
|
21
|
+
s.email = %w[david@negativecode.com]
|
21
22
|
s.homepage = "http://www.getvines.com"
|
22
23
|
|
23
|
-
s.files = FileList['[A-Z]*', '{bin,lib,conf,web}/**/*']
|
24
24
|
s.test_files = FileList["test/**/*"]
|
25
25
|
s.executables = %w[vines-services]
|
26
26
|
s.require_path = "lib"
|
27
27
|
|
28
|
-
s.add_dependency "bcrypt-ruby", "~> 3.0.
|
29
|
-
s.add_dependency "blather", "~> 0.5.
|
28
|
+
s.add_dependency "bcrypt-ruby", "~> 3.0.1"
|
29
|
+
s.add_dependency "blather", "~> 0.5.8"
|
30
30
|
s.add_dependency "citrus", "~> 2.4.0"
|
31
31
|
s.add_dependency "couchrest_model", "~> 1.1.2"
|
32
32
|
s.add_dependency "em-http-request", "~> 0.3.0"
|
@@ -39,8 +39,12 @@ and permissions are managed via the bundled web application."
|
|
39
39
|
s.required_ruby_version = '>= 1.9.2'
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
# Set gem file list after CoffeeScripts have been compiled, so web/javascripts/
|
43
|
+
# is included in the gem.
|
44
|
+
task :gemprep do
|
45
|
+
spec.files = FileList['[A-Z]*', '{bin,lib,conf,web}/**/*']
|
46
|
+
Gem::PackageTask.new(spec).define
|
47
|
+
Rake::Task['gem'].invoke
|
44
48
|
end
|
45
49
|
|
46
50
|
Rake::TestTask.new(:test) do |test|
|
@@ -116,7 +120,6 @@ task :compile do
|
|
116
120
|
|
117
121
|
sh %{coffee -c -b -o web/javascripts web/coffeescripts/*.coffee}
|
118
122
|
sh %{cat #{js_files} | uglifyjs -nc > web/javascripts/app.js}
|
119
|
-
|
120
123
|
sh %{cat #{css_files} > web/stylesheets/app.css}
|
121
124
|
end
|
122
125
|
|
@@ -126,5 +129,4 @@ task :cleanup do
|
|
126
129
|
File.delete('/tmp/index.html')
|
127
130
|
end
|
128
131
|
|
129
|
-
|
130
|
-
task :default => [:clobber, :test, :compile, :gem, :cleanup]
|
132
|
+
task :default => [:clobber, :test, :compile, :gemprep, :cleanup]
|
data/bin/vines-services
CHANGED
@@ -5,77 +5,96 @@ module Vines
|
|
5
5
|
module Command
|
6
6
|
class Init
|
7
7
|
def run(opts)
|
8
|
-
raise 'vines-services init
|
9
|
-
@domain = opts[:args].first.
|
10
|
-
|
11
|
-
@
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
raise 'vines-services init <domain>' unless opts[:args].size == 1
|
9
|
+
@domain = opts[:args].first.downcase
|
10
|
+
base = File.expand_path(@domain)
|
11
|
+
raise "Directory already initialized: #{@domain}" if File.exists?(base)
|
12
|
+
raise "Agent gem required: gem install vines-agent" unless agent_gem_installed?
|
13
|
+
|
14
|
+
EM.run do
|
15
|
+
Fiber.new do
|
16
|
+
@db = find_db
|
17
|
+
create_views
|
18
|
+
user = save_user(ask_for_jid, ask_for_password)
|
19
|
+
create_services(user)
|
20
|
+
@token = Kit.auth_token
|
21
|
+
|
22
|
+
Dir.mkdir(base)
|
23
|
+
%w[server services agent].each do |sub|
|
24
|
+
dir = File.expand_path(sub, base)
|
25
|
+
Dir.mkdir(dir)
|
26
|
+
Dir.chdir(dir) { send("init_#{sub}") }
|
27
|
+
end
|
28
|
+
puts "Initialized server, agent, and services directories: #{@domain}"
|
29
|
+
puts "Login at http://localhost:5280/"
|
30
|
+
EM.stop
|
31
|
+
end.resume
|
32
|
+
end
|
17
33
|
end
|
18
34
|
|
19
35
|
private
|
20
36
|
|
21
|
-
def
|
22
|
-
Dir.chdir(File.join(@domain, "server"))
|
37
|
+
def init_server
|
23
38
|
`vines init #{@domain}`
|
24
|
-
|
25
|
-
FileUtils.mv Dir.glob("#{@domain}/*"), "./"
|
39
|
+
FileUtils.mv(Dir.glob("#{@domain}/*"), '.')
|
26
40
|
FileUtils.remove_dir(@domain)
|
41
|
+
FileUtils.remove_dir('data/users')
|
42
|
+
web = File.expand_path("../../../../../web", __FILE__)
|
43
|
+
FileUtils.cp_r(Dir.glob("#{web}/*"), 'web')
|
44
|
+
update_server_config('conf/config.rb')
|
27
45
|
`vines start -d`
|
28
|
-
|
29
|
-
FileUtils.cp_r(File.expand_path("../../../../../web/javascripts", __FILE__), File.join("web", "javascripts"))
|
30
|
-
FileUtils.cp_r(File.expand_path("../../../../../web/stylesheets", __FILE__), File.join("web", "stylesheets"))
|
31
|
-
FileUtils.cp_r(File.expand_path("../../../../../web/images", __FILE__), File.join("web", "images"))
|
32
|
-
FileUtils.cp_r(File.expand_path("../../../../../web/coffeescripts", __FILE__), File.join("web", "coffeescripts"))
|
46
|
+
puts "Started vines server: vines start -d"
|
33
47
|
end
|
34
48
|
|
35
|
-
def
|
36
|
-
Dir.chdir("../agent/")
|
49
|
+
def init_agent
|
37
50
|
`vines-agent init #{@domain}`
|
38
|
-
FileUtils.
|
39
|
-
agent_password = Kit.generate_password
|
40
|
-
jid = Vines::JID.new(fqdn, @domain).bare
|
41
|
-
id = "user:#{jid}"
|
42
|
-
Fiber.new do
|
43
|
-
user = Vines::Services::CouchModels::User.new(id: id)
|
44
|
-
user.name = jid
|
45
|
-
user.system = true
|
46
|
-
user.password = agent_password
|
47
|
-
if user.valid?
|
48
|
-
user.save
|
49
|
-
log.debug("Saving user: #{user}")
|
50
|
-
end
|
51
|
-
end.resume
|
52
|
-
update_agent_config(File.join("#{@domain}", "conf", "config.rb"), agent_password)
|
53
|
-
FileUtils.mv Dir.glob("#{@domain}/*"), "./"
|
51
|
+
FileUtils.mv(Dir.glob("#{@domain}/*"), '.')
|
54
52
|
FileUtils.remove_dir(@domain)
|
53
|
+
FileUtils.cp("../server/conf/certs/#{@domain}.crt", 'conf/certs')
|
54
|
+
require File.expand_path('conf/config.rb')
|
55
|
+
token = Vines::Agent::Config.instance.domain.password
|
56
|
+
save_user(Vines::JID.new(fqdn, @domain), token, true)
|
55
57
|
`vines-agent start -d`
|
58
|
+
puts "Started vines agent: vines-agent start -d"
|
56
59
|
end
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
log, pid = %w[log pid data data/index].map do |sub|
|
65
|
-
File.join(sub).tap {|subdir| Dir.mkdir(subdir) }
|
66
|
-
end
|
67
|
-
update_config(File.join("conf", "config.rb"))
|
61
|
+
def init_services
|
62
|
+
%w[log pid data data/index data/upload].each {|sub| Dir.mkdir(sub) }
|
63
|
+
%w[data data/index data/upload].each {|dir| File.chmod(0700, dir) }
|
64
|
+
FileUtils.cp_r(File.expand_path("../../../../../conf", __FILE__), '.')
|
65
|
+
File.chmod(0600, 'conf/config.rb')
|
66
|
+
update_services_config('conf/config.rb')
|
68
67
|
`vines-services start -d`
|
68
|
+
puts "Started vines services component: vines-services start -d"
|
69
69
|
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
def agent_gem_installed?
|
72
|
+
require 'vines/agent'
|
73
|
+
rescue LoadError
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_views
|
78
|
+
url = "http://#{@db[:host]}:#{@db[:port]}"
|
79
|
+
CouchRest::Model::Base.database = CouchRest::Server.new(url).database(@db[:name])
|
80
|
+
CouchRest::Model::Base.subclasses.each do |klass|
|
81
|
+
klass.save_design_doc! if klass.respond_to?(:save_design_doc!)
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
85
|
+
def find_db
|
86
|
+
host, port = 'localhost', 5984
|
87
|
+
db = @domain.downcase.gsub('.', '_')
|
88
|
+
until create_db(host, port, db)
|
89
|
+
puts "CouchDB connection failed"
|
90
|
+
$stdout.write('CouchDB Host: ')
|
91
|
+
host = $stdin.gets.chomp
|
92
|
+
$stdout.write('CouchDB Port: ')
|
93
|
+
port = $stdin.gets.chomp
|
94
|
+
end
|
95
|
+
{host: host, port: port, name: db}
|
96
|
+
end
|
97
|
+
|
79
98
|
def create_db(host, port, db)
|
80
99
|
url = "http://#{host}:#{port}/#{db}"
|
81
100
|
begin
|
@@ -88,122 +107,120 @@ module Vines
|
|
88
107
|
false
|
89
108
|
end
|
90
109
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
server = $stdin.gets.chomp
|
101
|
-
$stdout.write('CouchDB Port: ')
|
102
|
-
port = $stdin.gets.chomp
|
103
|
-
end
|
104
|
-
@couch_server = server
|
105
|
-
@couch_port = port
|
106
|
-
@couch_db = db_name
|
107
|
-
update_config(File.join(@domain, "conf", "config.rb"))
|
108
|
-
@storage = Vines::Services::Storage::CouchDB.new do
|
109
|
-
host server
|
110
|
-
port port
|
111
|
-
database db_name
|
112
|
-
index_dir '.'
|
113
|
-
end
|
114
|
-
create_user
|
110
|
+
def save_user(jid, password, system=false)
|
111
|
+
user = Vines::Services::CouchModels::User.new.tap do |u|
|
112
|
+
u.id = "user:#{jid}"
|
113
|
+
u.plain_password = password
|
114
|
+
u.system = system
|
115
|
+
u.admin! unless system
|
116
|
+
end.save
|
117
|
+
puts "Created user: #{jid}"
|
118
|
+
user
|
115
119
|
end
|
116
120
|
|
117
|
-
|
118
|
-
|
119
|
-
jid, password = nil, nil
|
121
|
+
def ask_for_jid
|
122
|
+
jid = nil
|
120
123
|
until jid
|
121
124
|
$stdout.write('JID: ')
|
122
125
|
if node = $stdin.gets.chomp.split('@').first
|
123
126
|
jid = Vines::JID.new(node, @domain) rescue nil
|
124
127
|
end
|
125
128
|
end
|
129
|
+
jid
|
130
|
+
end
|
126
131
|
|
132
|
+
def ask_for_password
|
133
|
+
password = nil
|
127
134
|
until password
|
128
135
|
$stdout.write('Password: ')
|
129
136
|
`stty -echo`
|
130
137
|
password = $stdin.gets.chomp
|
131
138
|
password = nil if password.empty?
|
132
139
|
`stty echo`
|
140
|
+
puts
|
141
|
+
end
|
142
|
+
password
|
143
|
+
end
|
144
|
+
|
145
|
+
# Return the fully qualified domain name for this machine. This is used
|
146
|
+
# to determine the agent's JID.
|
147
|
+
def fqdn
|
148
|
+
ohai.fqdn.downcase
|
149
|
+
end
|
150
|
+
|
151
|
+
def ohai
|
152
|
+
require 'ohai'
|
153
|
+
@ohai ||= Ohai::System.new.tap do |system|
|
154
|
+
system.require_plugin('os')
|
155
|
+
system.require_plugin('hostname')
|
156
|
+
system.require_plugin('network')
|
157
|
+
system.require_plugin('platform')
|
133
158
|
end
|
134
|
-
puts "\nCreated #{jid}"
|
135
|
-
|
136
|
-
id = "user:#{jid}"
|
137
|
-
Fiber.new do
|
138
|
-
user = Vines::Services::CouchModels::User.new(id: id)
|
139
|
-
user.name = jid
|
140
|
-
user.password = password
|
141
|
-
if user.valid?
|
142
|
-
user.save
|
143
|
-
log.debug("\nSaving user: #{user}")
|
144
|
-
end
|
145
|
-
end.resume
|
146
159
|
end
|
147
160
|
|
148
|
-
#
|
149
|
-
|
161
|
+
# Create some default services based on this system's information. This
|
162
|
+
# gives the user some examples from which to build.
|
163
|
+
def create_services(user)
|
164
|
+
require 'etc'
|
165
|
+
ip = ohai.ipaddress.match(/(\d*\.\d*\.\d*)\.\d*/)[1]
|
166
|
+
platform = case ohai.platform
|
167
|
+
when 'mac_os_x' then 'Mac OS X'
|
168
|
+
else ohai.platform.capitalize
|
169
|
+
end
|
170
|
+
{
|
171
|
+
"All Systems" => "uptime_seconds > 0",
|
172
|
+
"#{ohai.kernel['name']} Systems" => "kernel.name is '#{ohai.kernel['name']}'",
|
173
|
+
"Domain: #{ohai.domain}" => "domain is '#{ohai.domain}'",
|
174
|
+
"#{platform} Systems" => "platform is '#{ohai.platform}'",
|
175
|
+
"Network: #{ip}.x" => "ipaddress starts with '#{ip}.'"
|
176
|
+
}.each do |name, code|
|
177
|
+
Vines::Services::CouchModels::Service.new(
|
178
|
+
name: name,
|
179
|
+
jid: to_jid(name),
|
180
|
+
code: code,
|
181
|
+
users: [user.jid],
|
182
|
+
accounts: [Etc.getlogin]).save
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_jid(name)
|
187
|
+
Blather::JID.new(CGI.escape(name), "vines.#{@domain}").to_s.downcase
|
188
|
+
end
|
189
|
+
|
190
|
+
def update_services_config(config)
|
150
191
|
text = File.read(config)
|
151
192
|
File.open(config, 'w') do |f|
|
152
|
-
|
193
|
+
replaced = text
|
194
|
+
.gsub('wonderland.lit', @domain.downcase)
|
195
|
+
.gsub('secr3t', @token)
|
196
|
+
.gsub("host 'localhost'", "host '#{@db[:host]}'")
|
197
|
+
.gsub("port 5984", "port #{@db[:port]}")
|
198
|
+
.gsub("database 'xmpp'", "database '#{@db[:name]}'")
|
199
|
+
f.write(replaced)
|
153
200
|
end
|
154
201
|
end
|
155
202
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
# FIXME vines.local
|
169
|
-
orig_config = """
|
170
|
-
host 'vines.local' do
|
171
|
-
cross_domain_messages false
|
172
|
-
private_storage false
|
173
|
-
storage 'fs' do
|
174
|
-
dir 'data/users'
|
175
|
-
end
|
176
|
-
# components 'tea' => 'secr3t',
|
177
|
-
# 'cake' => 'passw0rd'
|
178
|
-
end"""
|
179
|
-
new_config = """
|
180
|
-
host '#{@domain}' do
|
181
|
-
cross_domain_messages false
|
182
|
-
private_storage true
|
183
|
-
storage 'couchdb' do
|
184
|
-
host '#{@couch_server}'
|
185
|
-
port #{@couch_port}
|
186
|
-
database '#{@couch_db}'
|
187
|
-
tls false
|
188
|
-
username ''
|
189
|
-
password ''
|
190
|
-
end
|
191
|
-
components 'vines' => '#{@component_password}'
|
192
|
-
end"""
|
203
|
+
def update_server_config(config)
|
204
|
+
replacement = %Q{
|
205
|
+
storage 'couchdb' do
|
206
|
+
host '#{@db[:host]}'
|
207
|
+
port #{@db[:port]}
|
208
|
+
database '#{@db[:name]}'
|
209
|
+
tls false
|
210
|
+
username ''
|
211
|
+
password ''
|
212
|
+
end
|
213
|
+
components 'vines' => '#{@token}'
|
214
|
+
}
|
193
215
|
text = File.read(config)
|
194
|
-
text = text.gsub(orig_config, new_config)
|
195
|
-
text = text.gsub("host 'vines.wonderland.lit'", "host 'vines.#{@domain}'")
|
196
|
-
text = text.gsub("secr3t", "#{@component_password}")
|
197
|
-
text = text.gsub("host 'localhost'", "host '#{@couch_server}'")
|
198
|
-
text = text.gsub("port 5984", "port #{@couch_port}")
|
199
|
-
text = text.gsub("database 'xmpp'", "database '#{@couch_db}'")
|
200
216
|
File.open(config, 'w') do |f|
|
201
|
-
|
217
|
+
replaced = text
|
218
|
+
.gsub('Vines::Config.configure do', "require 'vines/services/roster'\n\nVines::Config.configure do")
|
219
|
+
.gsub(/\s{4}storage 'fs' do.*\s{4}end/m, replacement)
|
220
|
+
f.write(replaced)
|
202
221
|
end
|
203
222
|
end
|
204
223
|
end
|
205
224
|
end
|
206
225
|
end
|
207
226
|
end
|
208
|
-
|
209
|
-
|
@@ -19,18 +19,23 @@ module Vines
|
|
19
19
|
private
|
20
20
|
|
21
21
|
# Forward the agent's response message to the user that sent it the
|
22
|
-
# command.
|
22
|
+
# command. Tag the message with a jid element, identifying the agent
|
23
|
+
# returning the command's output, like:
|
24
|
+
# <jid xmlns="http://getvines.com/protocol">
|
25
|
+
# www01.wonderland.lit@wonderland.lit/vines
|
26
|
+
# </jid>.
|
23
27
|
def forward_to_user
|
24
28
|
jid = node.xpath('/message/ns:jid', 'ns' => NS).first
|
25
29
|
return unless jid
|
26
|
-
|
30
|
+
agent = node.from
|
27
31
|
node.from = node.to
|
28
32
|
node.to = jid.content
|
33
|
+
jid.content = agent
|
29
34
|
stream.write(node)
|
30
35
|
end
|
31
36
|
|
32
37
|
# Forward the user's message to the members of the service. Tag the
|
33
|
-
# message with a jid element, identifying the user executing the command
|
38
|
+
# message with a jid element, identifying the user executing the command,
|
34
39
|
# like: <jid xmlns="http://getvines.com/protocol">alice@wonderland.lit/tea</jid>
|
35
40
|
# When the agent receives a message from one of its services, it checks
|
36
41
|
# this jid element for the user permissions with which to run the command.
|
@@ -39,28 +39,29 @@ module Vines
|
|
39
39
|
raise 'user already exists' if User.find(id)
|
40
40
|
User.new(id: id).tap do |u|
|
41
41
|
u.system = system
|
42
|
-
u.
|
42
|
+
u.plain_password = password1
|
43
43
|
end
|
44
44
|
else # existing user
|
45
45
|
User.get!("user:%s" % Vines::JID.new(jid)).tap do |u|
|
46
46
|
raise "record found, but is a #{u.system ? 'system' : 'user'}" unless u.system == system
|
47
47
|
if system
|
48
|
-
u.
|
48
|
+
u.plain_password = password1 unless password1.empty?
|
49
49
|
else # humans
|
50
|
-
u.
|
50
|
+
if u.jid == current_user.jid
|
51
|
+
u.change_password(password1, password2) unless password1.empty? || password2.empty?
|
52
|
+
else
|
53
|
+
u.plain_password = password1 unless password1.empty?
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
55
59
|
unless system
|
56
60
|
user.name = name
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
send_error('not-acceptable')
|
61
|
-
end
|
61
|
+
# users can't set their own permissions
|
62
|
+
unless user.jid == current_user.jid
|
63
|
+
user.permissions = obj['permissions']
|
62
64
|
end
|
63
|
-
user.permissions = obj['permissions']
|
64
65
|
end
|
65
66
|
|
66
67
|
if user.valid?
|
@@ -85,8 +86,7 @@ module Vines
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def save_services(user, services)
|
88
|
-
return if user.system?
|
89
|
-
return unless user.permissions['manage_services']
|
89
|
+
return if user.system? || !user.manage_services?
|
90
90
|
current = user.services.map {|s| s.id }
|
91
91
|
members = []
|
92
92
|
|
@@ -6,7 +6,7 @@ module Vines
|
|
6
6
|
# Behaves just like EM::Queue with the exception that items added to the queue
|
7
7
|
# are sorted and popped from the queue by their priority. The optional comparator
|
8
8
|
# block passed in the constructor determines element priority, with items sorted
|
9
|
-
#
|
9
|
+
# lowest to highest. If no block is provided, the elements' natural ordering,
|
10
10
|
# via <=>, is used.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -14,15 +14,15 @@ module Vines
|
|
14
14
|
# q = PriorityQueue.new do |a, b|
|
15
15
|
# a[:priority] <=> b[:priority]
|
16
16
|
# end
|
17
|
-
# q.push({:priority => 1, :msg => 'one'})
|
18
|
-
# q.push({:priority => 2, :msg => 'two'})
|
19
17
|
# q.push({:priority => 3, :msg => 'three'})
|
18
|
+
# q.push({:priority => 2, :msg => 'two'})
|
19
|
+
# q.push({:priority => 1, :msg => 'one'})
|
20
20
|
# 3.times do
|
21
21
|
# q.pop {|item| puts item[:msg] }
|
22
22
|
# end
|
23
|
-
# #=> "
|
23
|
+
# #=> "one"
|
24
24
|
# # "two"
|
25
|
-
# # "
|
25
|
+
# # "three"
|
26
26
|
#
|
27
27
|
class PriorityQueue < EM::Queue
|
28
28
|
def initialize(&comparator)
|
@@ -30,7 +30,7 @@ module Vines
|
|
30
30
|
@items = Heap.new(&comparator)
|
31
31
|
end
|
32
32
|
|
33
|
-
# A binary
|
33
|
+
# A binary min heap implementation for efficient storage of queue items. This
|
34
34
|
# class implements the Array methods called by EM::Queue so that it may
|
35
35
|
# replace the +@items+ instance variable. Namely, +push+ +shift+, +size+, and
|
36
36
|
# +empty?+ are implemented.
|
@@ -42,7 +42,7 @@ module Vines
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def push(*items)
|
45
|
-
items.
|
45
|
+
items.each do |item|
|
46
46
|
@heap << item
|
47
47
|
move_up(@heap.size - 1)
|
48
48
|
end
|
@@ -73,17 +73,17 @@ module Vines
|
|
73
73
|
left = 2 * k + 1
|
74
74
|
right = 2 * k + 2
|
75
75
|
return if left > (@heap.size - 1)
|
76
|
-
|
77
|
-
if @comp[@heap[k], @heap[
|
78
|
-
@heap[k], @heap[
|
79
|
-
move_down(
|
76
|
+
smaller = (right < @heap.size && @comp[@heap[right], @heap[left]] < 0) ? right : left
|
77
|
+
if @comp[@heap[k], @heap[smaller]] > 0
|
78
|
+
@heap[k], @heap[smaller] = @heap[smaller], @heap[k]
|
79
|
+
move_down(smaller)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
83
|
def move_up(k)
|
84
84
|
return if k == 0
|
85
85
|
parent = (k - 1) / 2
|
86
|
-
if @comp[@heap[k], @heap[parent]]
|
86
|
+
if @comp[@heap[k], @heap[parent]] < 0
|
87
87
|
@heap[k], @heap[parent] = @heap[parent], @heap[k]
|
88
88
|
move_up(parent)
|
89
89
|
end
|