Class: Fenris::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/fenris/client.rb

Constant Summary collapse

UPDATE_INTERVAL =
10

Instance Method Summary collapse

Constructor Details

#initialize(url, options = OpenStruct.new) ⇒ Client

Returns a new instance of Client.



13
14
15
16
17
18
19
# File 'lib/fenris/client.rb', line 13

def initialize(url, options = OpenStruct.new)
  @url = url
  @url = URI.parse(url) unless url.is_a? URI
  options = OpenStruct.new options if options.is_a? Hash
  @options = options
  @verbose = false
end

Instance Method Details

#add(name) ⇒ Object



214
215
216
217
# File 'lib/fenris/client.rb', line 214

def add(name)
  update_user_config
  RestClient.post("#{url}consumers", { :name => name }, :content_type => :json, :accept => :json);
end

#bind(name, binding) ⇒ Object



242
243
244
245
# File 'lib/fenris/client.rb', line 242

def bind(name, binding)
  update_user_config
  RestClient.put("#{url}providers/#{name}", { :binding => binding }, :content_type => :json, :accept => :json);
end

#bind_locationObject



193
194
195
# File 'lib/fenris/client.rb', line 193

def bind_location
  "0.0.0.0:#{options.port}"
end

#cert(name) ⇒ Object



86
87
88
# File 'lib/fenris/client.rb', line 86

def cert(name)
  read_config_file "#{user_name}:#{name}.crt"
end

#cert_path(name) ⇒ Object



90
91
92
# File 'lib/fenris/client.rb', line 90

def cert_path(name)
  "#{config_dir}/#{user_name}:#{name}.crt"
end

#config_dirObject



43
44
45
# File 'lib/fenris/client.rb', line 43

def config_dir
  options.config
end

#consume(arg = nil, &blk) ⇒ Object



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/fenris/client.rb', line 306

def consume(arg = nil, &blk)
  update_config

  map = arg if arg.is_a? Hash
  map = { arg => provider_map[arg] } if arg.is_a? String
  map ||= provider_map

  EventMachine::run do
    EventMachine::PeriodicTimer.new(UPDATE_INTERVAL) { update_config(false) } if options.autosync
    map.each do |provider, binding|
      ConsumerLocal.begin(self, location_of(provider), provider, binding)
    end
    blk.call if blk
  end
end

#consumersObject



197
198
199
# File 'lib/fenris/client.rb', line 197

def consumers
  user["consumers"]
end

#debug(message) ⇒ Object



137
138
139
# File 'lib/fenris/client.rb', line 137

def debug(message)
  puts "DEBUG: #{message}" if options.debug and not options.quiet
end

#digest(obj) ⇒ Object



247
248
249
# File 'lib/fenris/client.rb', line 247

def digest obj
  OpenSSL::Digest::SHA1.new(obj.to_der).to_s
end

#exec(*args) ⇒ Object



322
323
324
325
326
327
328
329
330
# File 'lib/fenris/client.rb', line 322

def exec(*args)
  command = args.join(' ')
  consume do
    Thread.new do
      system command
      EventMachine::stop
    end
  end
end

#generate_csr(cn) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
# File 'lib/fenris/client.rb', line 251

def generate_csr(cn)
  subject = OpenSSL::X509::Name.parse "/DC=org/DC=fenris/CN=#{cn}"
  digest = OpenSSL::Digest::SHA1.new
  req = OpenSSL::X509::Request.new
  req.version = 0
  req.subject = subject
  req.public_key = key.public_key
  req.sign(key, digest)
  log "generating csr        #{digest req} #{subject}"
  req
end

#get_authkey_from_stdinObject



33
34
35
36
37
38
39
40
41
# File 'lib/fenris/client.rb', line 33

def get_authkey_from_stdin
  begin
    system "stty -echo"
    print "Authkey: "
    $stdin.gets.chomp
  ensure
    system "stty echo"
  end
end

#get_cn(cert) ⇒ Object



263
264
265
# File 'lib/fenris/client.rb', line 263

def get_cn(cert)
  cert.subject.to_a.detect { |a,b,c| a == "CN" }[1] rescue nil
end

#keyObject



291
292
293
# File 'lib/fenris/client.rb', line 291

def key
  read_config_file "#{user_name}.key"
end

#locationObject



189
190
191
# File 'lib/fenris/client.rb', line 189

def location
  "#{options.host}:#{options.port}"
end

#location_of(provider) ⇒ Object



181
182
183
184
185
186
187
# File 'lib/fenris/client.rb', line 181

def location_of(provider)
  if p = providers.detect { |p| p["name"] == provider }
    p["locations"].first
  else
    raise NoSuchProvider, provider
  end
end

#log(message) ⇒ Object



141
142
143
# File 'lib/fenris/client.rb', line 141

def log(message)
  puts "LOG: #{message}" if not options.quiet
end

#my_certObject



78
79
80
# File 'lib/fenris/client.rb', line 78

def my_cert
  read_config_file "#{user_name}.crt"
end

#my_cert_pathObject



94
95
96
# File 'lib/fenris/client.rb', line 94

def my_cert_path
  "#{config_dir}/#{user_name}.crt"
end

#my_keyObject



82
83
84
# File 'lib/fenris/client.rb', line 82

def my_key
  read_config_file "#{user_name}.key"
end

#my_key_pathObject



98
99
100
# File 'lib/fenris/client.rb', line 98

def my_key_path
  "#{config_dir}/#{user_name}.key"
end

#optionsObject



21
22
23
# File 'lib/fenris/client.rb', line 21

def options
  @options
end

#post_locationObject



145
146
147
# File 'lib/fenris/client.rb', line 145

def post_location
  RestClient.put("#{url}", { :location => location }, :content_type => :json, :accept => :json)
end

#provide(local_binding) ⇒ Object



297
298
299
300
301
302
303
304
# File 'lib/fenris/client.rb', line 297

def provide(local_binding)
  update_config
  EventMachine::run do
    EventMachine::PeriodicTimer.new(UPDATE_INTERVAL) { Thread.new { update_config(false) } } if options.autosync
    post_location
    ProviderServer.begin(self, bind_location, local_binding)
  end
end

#provider_mapObject



177
178
179
# File 'lib/fenris/client.rb', line 177

def provider_map
  Hash[ * providers.map { |p| [ p["name"], p["binding"] ] }.flatten ]
end

#providersObject



201
202
203
# File 'lib/fenris/client.rb', line 201

def providers
  user["providers"]
end

#read_cert(cn) ⇒ Object



67
68
69
70
71
72
73
74
75
76
# File 'lib/fenris/client.rb', line 67

def read_cert cn
  if cert = read_config_file("#{cn}.crt")
    if cert.not_after > Time.now
      cert
    else
      log "cert expired          #{digest cert} :: #{cert.not_after} :: #{cn}"
      nil
    end
  end
end

#read_config_file(name) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/fenris/client.rb', line 112

def read_config_file name
  path = "#{config_dir}/#{name}"
  if not File.exists? path
    nil
  elsif name =~ /[.]json$/
    JSON.parse File.read(path)
  elsif name =~ /[.]crt$/
    OpenSSL::X509::Certificate.new File.read(path)
  elsif name =~ /[.]key$/
    OpenSSL::PKey::RSA.new File.read(path)
  else
    File.read path
  end
end

#rekeyObject



226
227
228
229
230
231
# File 'lib/fenris/client.rb', line 226

def rekey
  update_user_config
  newkey = RestClient.post("#{url}authkeys", { }, :content_type => :json, :accept => :json);
  write_config_file "#{user_name}.authkey", newkey
  newkey
end

#remove(name) ⇒ Object



209
210
211
212
# File 'lib/fenris/client.rb', line 209

def remove(name)
  update_user_config
  RestClient.delete("#{url}consumers/#{name}");
end

#rootObject



287
288
289
# File 'lib/fenris/client.rb', line 287

def root
  read_config_file "root.crt"
end

#ssl?Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/fenris/client.rb', line 153

def ssl?
  @url.scheme == "https"
end

#update_config(verbose = true) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/fenris/client.rb', line 163

def update_config(verbose = true)
  @verbose = verbose
  log "updating config in #{config_dir}" if @verbose
  update_user_config
  log "have update user config" if @verbose
  write_config_file "root.crt", RestClient.get("#{url}cert") ## TODO find a way to get this out of the connection info
  verify_private_key
  verify_cert user_name
  providers.each do |p|
    verify_cert "#{user_name}:#{p["name"]}"
  end
  @verbose = false
end

#update_user_configObject



157
158
159
160
161
# File 'lib/fenris/client.rb', line 157

def update_user_config
  @user = JSON.parse RestClient.get("#{url}", :content_type => :json, :accept => :json)
  write_config_file "#{user_name}.authkey", @url.password
  write user
end

#urlObject



25
26
27
28
29
30
31
# File 'lib/fenris/client.rb', line 25

def url
  @url.user = options.user
  @url.password = read_config_file("#{user_name}.authkey")
  @url.password ||= options.password
  @url.password ||= get_authkey_from_stdin
  @url
end

#userObject



149
150
151
# File 'lib/fenris/client.rb', line 149

def user
   @user ||= read_config_file "config.json"
end

#user_nameObject



205
206
207
# File 'lib/fenris/client.rb', line 205

def user_name
  options.user
end

#useradd(name) ⇒ Object



219
220
221
222
223
224
# File 'lib/fenris/client.rb', line 219

def useradd(name)
  update_user_config
  newuser = JSON.parse RestClient.post("#{url}users", { :name => name }, :content_type => :json, :accept => :json);
  write_config_file "#{newuser["name"]}.authkey", newuser["authkey"]
  newuser
end

#userdel(name) ⇒ Object



237
238
239
240
# File 'lib/fenris/client.rb', line 237

def userdel(name)
  update_user_config
  RestClient.delete("#{url}users/#{name}", :content_type => :json, :accept => :json);
end

#usersObject



233
234
235
# File 'lib/fenris/client.rb', line 233

def users
  user["subusers"]
end

#validate_peer(pem, peer_connection = nil, peer_name = nil) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/fenris/client.rb', line 267

def validate_peer(pem, peer_connection = nil, peer_name = nil)
  consumer_cert = OpenSSL::X509::Certificate.new(pem)
  cert_cn = get_cn(consumer_cert)
  valid_peer_names = [ peer_name ] if peer_name
  valid_peer_names ||= consumers.map { |c| c["name"] }
  consumer_cn,provider_cn = cert_cn.split ":"
  provider_cn_ok = !!user_name
  consumer_cn_ok = !!valid_peer_names.detect { |name| name == consumer_cn }
  cert_ok = !!consumer_cert.verify(root.public_key)
  log "Consumer Cert CN '#{cert_cn}' displays correct provider? #{provider_cn_ok}"
  log "Consumer Cert CN '#{cert_cn}' in allowed_list? #{consumer_cn_ok}"
  log "Consumer Cert Signed By Broker? '#{cert_ok}'"
  result = consumer_cn_ok and provider_cn_ok and cert_ok
  unless result
    log "Certificate verification failed.  connection closed [#{consumer_cn_ok}] [#{provider_cn_ok}] [#{cert_ok}]"
    peer_connection.close_connection if peer_connection
  end
  result
end

#verify_cert(cn) ⇒ Object



57
58
59
60
61
62
63
64
65
# File 'lib/fenris/client.rb', line 57

def verify_cert(cn)
  if cert = read_cert(cn)
    log "existing cert         #{digest cert} :: #{cert.not_after} :: #{cn}" if @verbose
  else
    cert = OpenSSL::X509::Certificate.new(RestClient.post("#{url}cert", :csr => generate_csr(cn)))
    write cert
    log "new cert received     #{digest cert} :: #{cert.not_after} :: #{cn}"
  end
end

#verify_private_keyObject



47
48
49
50
51
52
53
54
55
# File 'lib/fenris/client.rb', line 47

def verify_private_key
  if key = read_config_file("#{user_name}.key")
    log "using existing key    #{digest key}" if @verbose
  else
    key = OpenSSL::PKey::RSA.new(2048)
    write key
    log "new rsa key generated #{digest key}"
  end
end

#write(object) ⇒ Object



127
128
129
130
131
132
133
134
135
# File 'lib/fenris/client.rb', line 127

def write object
  if object.is_a? OpenSSL::X509::Certificate
    write_config_file "#{get_cn(object)}.crt", object.to_pem
  elsif object.is_a? OpenSSL::PKey::RSA
    write_config_file "#{user_name}.key", object.to_pem
  else
    write_config_file "#{user_name}.json", object.to_json
  end
end

#write_cert(cert) ⇒ Object



102
103
104
# File 'lib/fenris/client.rb', line 102

def write_cert(cert)
  write_config_file "#{get_cn(cert)}.crt", cert
end

#write_config_file(name, data) ⇒ Object



106
107
108
109
110
# File 'lib/fenris/client.rb', line 106

def write_config_file name, data
  File.umask 0077
  Dir.mkdir config_dir unless Dir.exists? config_dir
  File.open("#{config_dir}/#{name}","w") { |f| f.write(data) }
end