224 lines
11 KiB
Plaintext
224 lines
11 KiB
Plaintext
<% content_for :title, "Rules - #{@project.name}" %>
|
|
|
|
<div class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
|
|
<!-- Header -->
|
|
<div class="mb-8">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h1 class="text-3xl font-bold text-gray-900">Rules</h1>
|
|
<p class="mt-2 text-gray-600">Manage WAF rules for traffic filtering and control</p>
|
|
</div>
|
|
<div class="flex space-x-3">
|
|
<%= link_to "Add Network Range", new_network_range_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
|
|
<%= link_to "Create Rule", new_rule_path, class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700" %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
|
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
|
<div class="p-5">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-5 w-0 flex-1">
|
|
<dl>
|
|
<dt class="text-sm font-medium text-gray-500 truncate">Total Rules</dt>
|
|
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@rules.count) %></dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
|
<div class="p-5">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-6 w-6 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-5 w-0 flex-1">
|
|
<dl>
|
|
<dt class="text-sm font-medium text-gray-500 truncate">Active Rules</dt>
|
|
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@rules.active.count) %></dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
|
<div class="p-5">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-6 w-6 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-5 w-0 flex-1">
|
|
<dl>
|
|
<dt class="text-sm font-medium text-gray-500 truncate">Block Rules</dt>
|
|
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@rules.where(action: 'deny').count) %></dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
|
<div class="p-5">
|
|
<div class="flex items-center">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-6 w-6 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-5 w-0 flex-1">
|
|
<dl>
|
|
<dt class="text-sm font-medium text-gray-500 truncate">Expired Rules</dt>
|
|
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@rules.expired.count) %></dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rules List -->
|
|
<div class="bg-white shadow rounded-lg">
|
|
<div class="px-6 py-4 border-b border-gray-200">
|
|
<h3 class="text-lg font-medium text-gray-900">All Rules</h3>
|
|
</div>
|
|
|
|
<% if @rules.any? %>
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Rule</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Target</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created</th>
|
|
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
<% @rules.each do |rule| %>
|
|
<tr class="hover:bg-gray-50">
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<div class="flex items-center">
|
|
<div>
|
|
<div class="text-sm font-medium text-gray-900">
|
|
<%= link_to "Rule ##{rule.id}", rule_path(rule), class: "text-blue-600 hover:text-blue-900" %>
|
|
</div>
|
|
<div class="text-sm text-gray-500">
|
|
<%= rule.source.humanize %>
|
|
<% if rule.network_range? && rule.network_range %>
|
|
• <%= link_to rule.network_range.cidr, network_range_path(rule.network_range), class: "text-blue-600 hover:text-blue-900" %>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%=
|
|
case rule.rule_type
|
|
when 'network' then 'bg-blue-100 text-blue-800'
|
|
when 'rate_limit' then 'bg-yellow-100 text-yellow-800'
|
|
when 'path_pattern' then 'bg-purple-100 text-purple-800'
|
|
else 'bg-gray-100 text-gray-800'
|
|
end %>">
|
|
<%= rule.rule_type.humanize %>
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%=
|
|
case rule.action
|
|
when 'allow' then 'bg-green-100 text-green-800'
|
|
when 'deny' then 'bg-red-100 text-red-800'
|
|
when 'rate_limit' then 'bg-yellow-100 text-yellow-800'
|
|
when 'redirect' then 'bg-indigo-100 text-indigo-800'
|
|
when 'log' then 'bg-gray-100 text-gray-800'
|
|
else 'bg-gray-100 text-gray-800'
|
|
end %>">
|
|
<%= rule.action.upcase %>
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
|
<% if rule.network_range? && rule.network_range %>
|
|
<%= rule.network_range.cidr %>
|
|
<% if rule.network_range.company.present? %>
|
|
<div class="text-xs text-gray-500"><%= rule.network_range.company %></div>
|
|
<% end %>
|
|
<% elsif rule.conditions.present? %>
|
|
<div class="max-w-xs truncate">
|
|
<%= JSON.parse(rule.conditions || "{}").map { |k, v| "#{k}: #{v}" }.join(", ") rescue "Invalid JSON" %>
|
|
</div>
|
|
<% else %>
|
|
<span class="text-gray-400">-</span>
|
|
<% end %>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<div class="flex items-center space-x-2">
|
|
<% if rule.enabled? && !rule.expired? %>
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Active</span>
|
|
<% elsif rule.expired? %>
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">Expired</span>
|
|
<% else %>
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">Disabled</span>
|
|
<% end %>
|
|
|
|
<% if rule.expires_at.present? %>
|
|
<span class="text-xs text-gray-500" title="Expires at <%= rule.expires_at.strftime('%Y-%m-%d %H:%M') %>">
|
|
<%= distance_of_time_in_words(Time.current, rule.expires_at) %> left
|
|
</span>
|
|
<% end %>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
<%= time_ago_in_words(rule.created_at) %> ago
|
|
<div class="text-xs">
|
|
by <%= rule.user&.email_address || 'System' %>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
|
<%= link_to "View", rule_path(rule), class: "text-blue-600 hover:text-blue-900 mr-3" %>
|
|
<% if rule.enabled? %>
|
|
<%= link_to "Disable", disable_rule_path(rule),
|
|
method: :post,
|
|
data: { confirm: "Are you sure you want to disable this rule?" },
|
|
class: "text-yellow-600 hover:text-yellow-900 mr-3" %>
|
|
<% else %>
|
|
<%= link_to "Enable", enable_rule_path(rule),
|
|
method: :post,
|
|
class: "text-green-600 hover:text-green-900 mr-3" %>
|
|
<% end %>
|
|
<%= link_to "Edit", edit_rule_path(rule), class: "text-indigo-600 hover:text-indigo-900" %>
|
|
</td>
|
|
</tr>
|
|
<% end %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<% else %>
|
|
<div class="text-center py-12">
|
|
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
<h3 class="mt-2 text-sm font-medium text-gray-900">No rules</h3>
|
|
<p class="mt-1 text-sm text-gray-500">Get started by creating your first WAF rule.</p>
|
|
<div class="mt-6">
|
|
<%= link_to "Create Rule", new_rule_path, class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700" %>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</div> |