vines-services 0.1.0 → 0.1.1
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.
- 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
|