MarkController.php 9.68 KiB
<?php
namespace App\Http\Controllers;
use Cylab\Mark\Client;
use App\Mark;
use App\Feedback;
use Illuminate\Support\Facades\Auth;
class MarkController extends Controller
{
private function server() : Client
{
return Mark::get();
}
public function error()
{
return view('app.error');
}
public function status()
{
$status = $this->server()->status();
$status_history = $this->server()->history();
$history_memory = $this->extractPoints($status_history, "memory_used");
$history_load = $this->extractPoints($status_history, "load");
$history_jobs_executed = $this->extractPoints($status_history, "executor_jobs_executed");
$history_jobs_execution_rate = $this->computeExecutionRate($history_jobs_executed);
$history_db_records = $this->extractPoints($status_history, "db_data_count");
$history_db_records_rate = $this->computeExecutionRate($history_db_records);
$history_db_evidences = $this->extractPoints($status_history, "db_evidence_count");
$history_db_evidences_rate = $this->computeExecutionRate($history_db_evidences);
return view("app.status", [
"status" => $status,
"history_memory" => $history_memory,
"history_load" => $history_load,
"history_jobs_execution_rate" => $history_jobs_execution_rate,
"history_db_records_rate" => $history_db_records_rate,
"history_db_evidences_rate" => $history_db_evidences_rate]);
}
public function lastData()
{
return view("app.lastdata", ["data" => $this->server()->findLastData()]);
}
public function lastEvidences()
{
return view(
"app.lastevidences",
["evidences" => $this->server()->findLastEvidences()]
);
}
public function pause()
{
$this->server()->pause();
return redirect(action('MarkController@status'));
}
public function resume()
{
$this->server()->resume();
return redirect(action('MarkController@status'));
}
public function reload()
{
$this->server()->reload();
return redirect(action('MarkController@status'));
}
/**
* Find
* @param string $label
* @return type the detector corresponding to this label.
* Otherwize redirect to the detectors graph.
*/
private function findDetector(string $label)
{
$detectors = $this->server()->activation();
$detector = null;
foreach ($detectors as $d) {
if ($d->label == $label) {
$detector = $d;
break;
}
}
if ($detector === null) {
session()->flash(
'error',
$label . ' is not a detector and does not produce a ranking'
);
abort(redirect(action('MarkController@rankingHome')));
}
return $detector;
}
public function ranking(string $label)
{
$detector = $this->findDetector($label);
$evidences = $this->server()->findEvidence($label);
$now = \Carbon\Carbon::now();
return view(
"app.ranking",
[
"label" => $label,
"evidences" => $evidences,
"detector" => $detector,
"now" => $now]
);
}
public function rankingCSV(string $label)
{
$detector = $this->findDetector($label);
$evidences = $this->server()->findEvidence($label);
$now = \Carbon\Carbon::now();
$file_name = $now->toISOString() . "_$label.csv";
$headers = array(
"Content-type" => "text/csv",
"Content-Disposition" => "attachment; filename=$file_name",
"Pragma" => "no-cache",
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
"Expires" => "0"
);
$columns = ['id', 'subject', 'score', 'time'];
$callback = function () use ($evidences, $columns) {
$file = fopen('php://output', 'w');
fputcsv($file, $columns);
foreach ($evidences as $ev) {
fputcsv($file, [
$ev->id,
$ev->subject(),
$ev->score,
$ev->time
]);
}
fclose($file);
};
return response()->stream($callback, 200, $headers);
}
public function rankingHome()
{
$activation_graph_elements = [];
$detectors = $this->server()->activation();
// nodes
foreach ($detectors as $detector) {
// node
$activation_graph_elements[] = ["data" => ["id" => $detector->label]];
// edges
$sources = $this->findSources($detectors, $detector);
foreach ($sources as $source) {
$activation_graph_elements[] = ["data" => [
"id" => rand(),
"source" => $source,
"target" => $detector->label]];
}
if (count($sources) == 0) {
// the trigger_label for this detector is a data source
// => create node + edge
$activation_graph_elements[] = ["data" => ["id" => $detector->trigger_label]];
$activation_graph_elements[] = ["data" => [
"id" => rand(),
"source" => $detector->trigger_label,
"target" => $detector->label]];
}
}
return view("app.detectors", ["activation_graph_elements" => $activation_graph_elements]);
}
private function findSources(array $all_detectors, $detector)
{
$pattern = '/' . $detector->trigger_label . '/';
$sources = [];
foreach ($all_detectors as $d) {
if (preg_match($pattern, $d->label)) {
$sources[] = $d->label;
}
}
return $sources;
}
public function extractPoints(array $evidences, string $field) : array
{
$points = [];
foreach ($evidences as $evidence) {
// used by status history
// this is dirty :-(
// status history should return an array of objects
if (is_array($evidence)) {
$points[] = new \App\TimePoint(
$evidence["time"],
$evidence[$field]
);
} else {
$points[] = new \App\TimePoint(
$evidence->time,
$evidence->$field
);
}
}
return $points;
}
public function evidence(string $id)
{
$time_window = 3600; // in seconds
$ev = $this->server()->findEvidenceById($id);
$since = $ev->time - $time_window * 1000;
$references = [];
foreach ($ev->references as $id) {
$references[] = $this->server()->findEvidenceById($id);
}
$history = $this->server()->findEvidenceSince($ev->label, $ev->subject, $since);
$feedback = Feedback::findByReportId($ev->id);
return view(
'app.evidence',
[
"evidence" => $ev,
"history" => $history,
"history_points" => $this->extractPoints($history, "score"),
"feedback" => $feedback,
"references" => $references]
);
}
/**
* Mark an evidence as false alarm (whitelist).
* @param string $id
*/
public function falseAlarm(string $id)
{
$feedback = new Feedback();
$feedback->report_id = $id;
$feedback->is_true_alert = false;
$feedback->user_id = Auth::id();
$feedback->save();
return redirect(action('MarkController@evidence', ['id' => $id]));
}
/**
* Mark an evidence as false alarm (whitelist).
* @param string $id
*/
public function trueDetection(string $id)
{
$feedback = new Feedback();
$feedback->report_id = $id;
$feedback->is_true_alert = true;
$feedback->user_id = Auth::id();
$feedback->save();
return redirect(action('MarkController@evidence', ['id' => $id]));
}
public function evidenceData(string $id, $data_id)
{
$ev = $this->server()->findEvidenceById($id);
$query = $ev->requests[$data_id];
$data = $this->server->findDataByQuery($query);
return view('app.data', [
"evidence" => $ev,
"query" => $query,
"data" => $data
]);
}
/**
* Compute the number of jobs executed per minute.
*
* @param array $history_jobs_executed
* @return array
*/
private function computeExecutionRate(array $history_jobs_executed) : array
{
if (count($history_jobs_executed) < 2) {
// not enough points to compute execution rate...
return [];
}
$points = [];
$last_point = $history_jobs_executed[0];
foreach ($history_jobs_executed as $point) {
$delta_t_minutes = ($point->t - $last_point->t) / 60000;
// this point is less than a minute after the last computed point
if ($delta_t_minutes < 1) {
continue;
}
$delta_value = $point->y - $last_point->y;
if ($delta_value < 0) {
$delta_value = 0;
}
$points[] = new \App\TimePoint(
$point->t,
round($delta_value / $delta_t_minutes)
);
$last_point = $point;
}
return $points;
}
}