2013-02-01 03:48:27 +00:00
class MacsController < ApplicationController
2013-10-26 10:22:03 +00:00
load_and_authorize_resource :mac , :except = > [ :create , :history ]
2013-09-29 02:36:23 +00:00
#load_and_authorize_resource :user, :through => :mac, :except => [:index, :show, :scan, :import]
2013-02-01 03:33:40 +00:00
2013-09-24 01:51:22 +00:00
before_filter :arp_lookup , :only = > :new
2013-02-01 03:33:40 +00:00
#require "active_record"
require " optparse "
#require "rubygems"
def index
2013-09-27 07:56:52 +00:00
recent_mac_logs_ungrouped = MacLog . last ( 1000 )
2013-09-28 11:59:19 +00:00
if recent_mac_logs_ungrouped . present?
@mac_time_start_date = recent_mac_logs_ungrouped . first . created_at
recent_mac_logs = recent_mac_logs_ungrouped . group_by ( & :mac )
@mac_times = { }
# Go thru each mac
recent_mac_logs . each do | mac_log |
last_active = nil
# And the entries for each mac (mac_log.first is the string, mac_log.last is the array)
mac_log . last . each do | entry |
# Find an activate followed immediately by a deactivate
if entry . action == " activate "
last_active = entry
else
if last_active && entry . action == " deactivate "
# Calculate the time difference between the two and append to this mac's total time
this_entry = @mac_times [ entry . mac ]
if this_entry
this_time = this_entry [ :time ]
else
this_time = 0
end
@mac_times [ entry . mac ] = { :mac = > entry , :time = > ( entry . created_at - last_active . created_at ) + this_time }
2013-09-27 07:56:52 +00:00
else
2013-09-28 11:59:19 +00:00
# No pair found; discard.
last_active = nil
2013-09-27 07:56:52 +00:00
end
end
end
end
2013-09-28 11:59:19 +00:00
@mac_times_sorted = @mac_times . sort { | a , b | b . last [ :time ] < = > a . last [ :time ] }
@most_active_mac = nil
@runner_up_mac = nil
@mac_times_sorted . each do | mac_time |
unless @most_active_mac
2013-09-27 07:56:52 +00:00
this_mac = Mac . find_by_mac ( mac_time . first )
unless this_mac . hidden
2013-09-28 11:59:19 +00:00
@most_active_mac = this_mac
@most_active = mac_time
end
else
unless @runner_up_mac
this_mac = Mac . find_by_mac ( mac_time . first )
unless this_mac . hidden
@runner_up_mac = this_mac
@runner_up = mac_time
end
2013-09-27 07:56:52 +00:00
end
end
end
end
2013-09-28 11:59:19 +00:00
2013-02-01 10:37:30 +00:00
#@active_macs = Mac.where(:active => true, :hidden => false)
#@active_macs += Mac.where(:active => true, :hidden => nil)
# De-dupe users for the public
if can? :update , Mac then
@active_macs = Mac . where ( " macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?) " , true , false ) . includes ( :user ) . order ( " users.name ASC " )
2013-02-01 10:58:26 +00:00
elsif user_signed_in? then
2013-02-01 10:37:30 +00:00
@active_macs = Mac . where ( " macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?) " , true , false ) . includes ( :user ) . order ( " users.name ASC " ) . group ( " users.name " )
2013-02-01 10:58:26 +00:00
else
2013-09-22 08:08:05 +00:00
@active_macs = Mac . select ( " mac, note, user_id " ) . where ( " macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?) " , true , false ) . joins ( :user ) . order ( " users.name ASC " ) . group ( " users.name, mac, note, user_id " )
2013-02-01 10:37:30 +00:00
end
@hidden_macs = Mac . where ( " macs.active = ? AND macs.hidden = ? " , true , true ) . order ( " note ASC " )
2013-02-01 07:06:13 +00:00
@all_macs = Mac . find ( :all , :order = > " LOWER(mac) " )
2013-02-01 10:58:26 +00:00
respond_to do | format |
format . html
format . json {
2013-02-09 12:05:06 +00:00
@filtered_macs = Mac . select ( " macs.mac, users.name, macs.note " ) . where ( " macs.active = ? AND (macs.hidden IS NULL OR macs.hidden = ?) " , true , false ) . joins ( :user )
2013-02-01 10:58:26 +00:00
render :json = > @filtered_macs
}
end
2013-02-01 03:33:40 +00:00
end
2013-10-26 10:22:03 +00:00
def history
authorize! :read_details , Mac
begin
@start_date = DateTime . parse ( params [ :start ] )
@end_date = DateTime . parse ( params [ :end ] )
rescue
@start_date = DateTime . now - 2 . weeks
@end_date = DateTime . now
end
@mac_logs_by_hour = MacLog . where ( " created_at > ? AND created_at < ? " , @start_date , @end_date ) . group_by { | m | m . created_at . beginning_of_hour }
@mac_log_graph = [ ]
mac_running_balance = 0
lowest_balance = 0
@mac_logs_by_hour . each do | time , mac_log |
mac_log . each do | entry |
# Add one computer for activate, subtract one for deactivate
if entry . action == " activate "
mac_running_balance += 1
elsif entry . action == " deactivate "
mac_running_balance -= 1
end
# Keep track of the lowest number in the graph
if mac_running_balance < lowest_balance
lowest_balance = mac_running_balance
end
end
@mac_log_graph << [ time . to_time . to_i * 1000 , mac_running_balance ]
end
if lowest_balance != 0
# Subtract a negative balance to raise everything
@mac_log_graph = @mac_log_graph . map { | time , balance | [ time , balance - lowest_balance ] }
end
respond_to do | format |
format . html
format . json { render :json = > @mac_log_graph }
end
end
2013-02-01 03:33:40 +00:00
# GET /macs/1
# GET /macs/1.json
def show
@mac = Mac . find ( params [ :id ] )
2013-10-02 11:57:46 +00:00
@mac_logs = MacLog . where ( :mac = > @mac . mac )
2013-02-01 03:33:40 +00:00
respond_to do | format |
format . html # show.html.erb
format . json { render :json = > @macs }
end
end
# GET /macs/new
# GET /macs/new.json
def new
@mac = Mac . new
2013-02-01 10:37:30 +00:00
if can? :manage , Mac then
@users = User . accessible_by ( current_ability ) . sort_by ( & :name )
else
@users = [ current_user ]
end
2013-02-01 03:33:40 +00:00
respond_to do | format |
format . html # new.html.erb
format . json { render :json = > @mac }
end
end
# GET /macs/1/edit
def edit
@mac = Mac . find ( params [ :id ] )
2013-02-01 10:37:30 +00:00
if can? :manage , Mac then
@users = User . accessible_by ( current_ability ) . sort_by ( & :name )
else
@users = [ current_user ]
end
2013-02-01 03:33:40 +00:00
end
# POST /macs
def create
@mac = Mac . new ( params [ :mac ] )
2013-09-24 01:34:18 +00:00
@existing_mac = Mac . find_by_mac ( @mac . mac )
if can? :manage , Mac then
@users = User . accessible_by ( current_ability ) . sort_by ( & :name )
else
@users = [ current_user ]
end
if @existing_mac . present?
if @existing_mac . user_id . nil?
redirect_to edit_mac_path ( @existing_mac ) , :notice = > 'This MAC already exists, edit it here:'
else
@mac . errors . add ( :user , " for this MAC is already set to #{ @existing_mac . user . name } -- please contact them or an admin if this is incorrect. " )
render :action = > " new "
end
2013-02-01 03:33:40 +00:00
else
2013-09-24 01:34:18 +00:00
respond_to do | format |
if @mac . save
format . html { redirect_to macs_path , :notice = > 'MAC was successfully created.' }
format . json { render :json = > @mac , :status = > :created , :location = > @mac }
else
format . html { render :action = > " new " }
format . json { render :json = > @mac . errors , :status = > :unprocessable_entity }
end
end
2013-02-01 03:33:40 +00:00
end
end
# PUT /macs/1
# PUT /macs/1.json
def update
#Log who updated this
@mac = Mac . find ( params [ :id ] )
2013-02-09 09:08:29 +00:00
@mac . assign_attributes ( params [ :mac ] )
#@mac.user_id = params[:mac][:user_id]
2013-02-01 10:37:30 +00:00
authorize! :update , @mac
if can? :manage , Mac then
@users = User . accessible_by ( current_ability ) . sort_by ( & :name )
else
@users = [ current_user ]
end
2013-02-01 03:33:40 +00:00
respond_to do | format |
2013-02-01 10:37:30 +00:00
if @mac . save
2013-09-24 01:34:18 +00:00
format . html { redirect_to macs_path , :notice = > 'MAC was successfully updated.' }
2013-02-01 03:33:40 +00:00
format . json { head :no_content }
else
format . html { render :action = > " edit " }
format . json { render :json = > @mac . errors , :status = > :unprocessable_entity }
end
end
end
2013-09-24 01:51:22 +00:00
def arp_lookup
@ip = request . env [ 'REMOTE_ADDR' ]
2014-01-24 11:27:21 +00:00
@arp = / ([0-9A-F]{2}[:-]){5}([0-9A-F]{2}) /i . match ( %x( arp -a | grep #{ @ip } ) )
2013-09-24 01:51:22 +00:00
end
2013-02-01 03:33:40 +00:00
def scan
Rails . logger . info " starting scan... "
2013-09-26 04:45:12 +00:00
# Command line arguments
options = { } ;
OptionParser . new { | opts |
opts . banner = " Usage: pamela-scanner.rb --interface=en0 "
options [ :verbose ] = true
opts . on ( " v " , " --verbose " , " Run verbosely " ) { | verbose |
options [ :verbose ] = verbose
}
options [ :interface ] = " eth0 "
opts . on ( " i " , " --interface=interface " , " Network Interface " ) { | interface |
options [ :interface ] = interface
}
options [ :max_age ] = 20
opts . on ( " a " , " --max-age=minutes " , " Minutes to keep expired macs active " ) { | max_age |
options [ :max_age ] = max_age . to_i
}
options [ :db_host ] = " configure_me "
opts . on ( " r " , " --db-host=host " , " Database Host " ) { | host |
options [ :db_host ] = host
}
options [ :db_name ] = " configure_me "
opts . on ( " n " , " --db-name=name " , " Database Name " ) { | name |
options [ :db_name ] = name
}
options [ :db_user ] = " configure_me "
opts . on ( " u " , " --db-user=user " , " Database User " ) { | user |
options [ :db_user ] = user
}
options [ :db_password ] = " configure_me "
opts . on ( " p " , " --db-password=password " , " Database Password " ) { | password |
options [ :db_password ] = password
}
} . parse!
# Open the database
#ActiveRecord::Base::establish_connection(
# :adapter => "mysql2",
# :host => options[:db_host],
# :database => options[:db_name],
# :username => options[:db_user],
# :password => options[:db_password])
#class Mac < ActiveRecord::Base
#end
#class MacLog < ActiveRecord::Base
#end
# Scan the network for mac addresses
macs = { } ;
command = sprintf ( " arp-scan -R --interface=%s --localnet " , options [ :interface ] )
if options [ :verbose ]
Rails . logger . info " Running [ #{ command } ] "
end
IO . popen ( command ) { | stdin |
2014-03-13 03:46:24 +00:00
result = stdin . read ( )
result . lines . each { | line |
Rails . logger . info " Reading stdin: " + line . inspect
2013-09-26 04:45:12 +00:00
next if line !~ / ^([ \ d \ .]+) \ s+([[:xdigit:]:]+) \ s / ;
2013-09-27 07:56:52 +00:00
macs [ ( $2 ) . downcase ] = ( $1 ) . downcase ;
2013-09-26 04:45:12 +00:00
}
2014-03-13 03:46:24 +00:00
Rails . logger . info " STDIN: " + result . lines . count . inspect
@macs = macs . dup # make a copy for output in the view
Rails . logger . info " MACS: " + @macs . inspect
2013-09-26 04:45:12 +00:00
}
# Scan the existing macs and update each record as necessary
Mac . find ( :all ) . each { | entry |
mac = entry . mac . downcase
ip = entry . ip
2014-03-13 03:46:24 +00:00
if macs . has_key? ( mac ) # if our scan shows this mac
2013-09-26 04:45:12 +00:00
if ! entry . active || ! entry . since
Rails . logger . info " Activating #{ mac } at #{ ip } " if options [ :verbose ]
entry . since = Time . now
MacLog . new ( :mac = > mac , :ip = > ip , :action = > " activate " ) . save
end
entry . active = 1
entry . ip = ip
entry . refreshed = Time . now
entry . save
macs . delete ( mac )
next
end
# Entry is no longer current
if entry . active
ageMinutes = ( ( Time . now - entry . refreshed ) / 60 ) . to_i
next if ageMinutes < options [ :max_age ]
Rails . logger . info " Deactivating #{ mac } , #{ ageMinutes } minutes old " if options [ :verbose ]
entry . active = 0
entry . save
MacLog . new ( :mac = > mac , :ip = > ip , :action = > " deactivate " ) . save
end
}
# Add entries for any macs not already in the db
macs . each { | mac , ip |
Rails . logger . info " Activating new entry #{ mac } at #{ ip } " if options [ :verbose ]
Mac . new ( :mac = > mac , :ip = > ip , :active = > 1 , :since = > Time . now , :refreshed = > Time . now ) . save
Rails . logger . info MacLog . new ( :mac = > mac , :ip = > ip , :action = > " activate " ) . save
}
2013-02-01 03:33:40 +00:00
@log = MacLog . all
end #def scan
2013-02-01 07:06:13 +00:00
def import
require 'csv'
csv_text = File . read ( 'mac_log.csv' )
csv = CSV . parse ( csv_text )
@output = [ ]
csv . each do | row |
@output += [ row [ 1 ] , Mac . create ( { :mac = > row [ 0 ] , :note = > row [ 1 ] , :hidden = > row [ 2 ] } ) ]
end
end
2013-02-01 03:33:40 +00:00
end