From e447b754c33a851729fb8a4e00c043f863878ede Mon Sep 17 00:00:00 2001 From: Scott Schneider Date: Tue, 21 Apr 2015 11:35:23 -0700 Subject: [PATCH] Add basic HTTP authentication and /token routes - the only initial backend option for auth is LDAP --- lib/vmpooler/api/helpers.rb | 54 +++++++++++++++++++++++++++++ lib/vmpooler/api/reroute.rb | 12 +++++++ lib/vmpooler/api/v1.rb | 68 +++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/lib/vmpooler/api/helpers.rb b/lib/vmpooler/api/helpers.rb index 5ce6d7e..a618d15 100644 --- a/lib/vmpooler/api/helpers.rb +++ b/lib/vmpooler/api/helpers.rb @@ -4,6 +4,60 @@ module Vmpooler module Helpers + def protected! + return if authorized? + + content_type :json + + result = { 'ok' => false } + + headers['WWW-Authenticate'] = 'Basic realm="Authentication required"' + halt 401, JSON.pretty_generate(result) + end + + def authorized? + @auth ||= Rack::Auth::Basic::Request.new(request.env) + + if @auth.provided? and @auth.basic? and @auth.credentials + username, password = @auth.credentials + + if authenticate(Vmpooler::API.settings.config[:auth], username, password) + return true + end + end + + return false + end + + def authenticate(auth, username_str, password_str) + case auth['provider'] + when 'ldap' + require 'rubygems' + require 'net/ldap' + + ldap = Net::LDAP.new( + :host => auth[:ldap]['host'], + :port => auth[:ldap]['port'] || 389, + :encryption => { + :method => :start_tls, + :tls_options => { :ssl_version => 'TLSv1' } + }, + :base => auth[:ldap]['base'], + :auth => { + :method => :simple, + :username => "#{auth[:ldap]['user_object']}=#{username_str},#{auth[:ldap]['base']}", + :password => password_str + } + ) + + if ldap.bind + return true + end + end + + return false + end + def mean(list) s = list.map(&:to_f).reduce(:+).to_f (s > 0 && list.length > 0) ? s / list.length.to_f : 0 diff --git a/lib/vmpooler/api/reroute.rb b/lib/vmpooler/api/reroute.rb index 72832f5..0592de4 100644 --- a/lib/vmpooler/api/reroute.rb +++ b/lib/vmpooler/api/reroute.rb @@ -11,6 +11,18 @@ module Vmpooler call env.merge("PATH_INFO" => "/api/v#{api_version}/summary") end + post '/token/?' do + call env.merge("PATH_INFO" => "/api/v#{api_version}/token") + end + + get '/token/:token/?' do + call env.merge("PATH_INFO" => "/api/v#{api_version}/token/#{params[:token]}") + end + + delete '/token/:token/?' do + call env.merge("PATH_INFO" => "/api/v#{api_version}/token/#{params[:token]}") + end + get '/vm/?' do call env.merge("PATH_INFO" => "/api/v#{api_version}/vm") end diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index ae8ed8e..3ab536f 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -173,6 +173,74 @@ module Vmpooler JSON.pretty_generate(result) end + get "#{api_prefix}/token/:token/?" do + content_type :json + + status 404 + result = { 'ok' => false } + + if Vmpooler::API.settings.config[:auth] + status 401 + + protected! + + token = Vmpooler::API.settings.redis.hgetall('vmpooler__token__' + params[:token]) + + if not token.nil? and not token.empty? + status 200 + result = { 'ok' => true, params[:token] => token } + else + status 404 + end + end + + JSON.pretty_generate(result) + end + + delete "#{api_prefix}/token/:token/?" do + content_type :json + + status 404 + result = { 'ok' => false } + + if Vmpooler::API.settings.config[:auth] + status 401 + + protected! + + if Vmpooler::API.settings.redis.del('vmpooler__token__' + params[:token]).to_i > 0 + status 200 + result['ok'] = true + end + end + + JSON.pretty_generate(result) + end + + post "#{api_prefix}/token" do + content_type :json + + status 404 + result = { 'ok' => false } + + if Vmpooler::API.settings.config[:auth] + status 401 + + protected! + + o = [('a'..'z'), ('0'..'9')].map(&:to_a).flatten + result['token'] = o[rand(25)] + (0...31).map { o[rand(o.length)] }.join + + Vmpooler::API.settings.redis.hset('vmpooler__token__' + result['token'], 'user', @auth.username) + Vmpooler::API.settings.redis.hset('vmpooler__token__' + result['token'], 'timestamp', Time.now) + + status 200 + result['ok'] = true + end + + JSON.pretty_generate(result) + end + get "#{api_prefix}/vm/?" do content_type :json