Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions migrations/007.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
$migration_name = 'Add zone super administrator role';

$this->database->exec("ALTER TYPE access_level ADD VALUE IF NOT EXISTS 'zone-super-administrator'");
2 changes: 1 addition & 1 deletion model/migrationdirectory.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class MigrationDirectory extends DBDirectory {
/**
* Increment this constant to activate a new migration from the migrations directory
*/
const LAST_MIGRATION = 6;
const LAST_MIGRATION = 7;

public function __construct() {
parent::__construct();
Expand Down
9 changes: 9 additions & 0 deletions model/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,15 @@ public function access_to(Zone $zone) {
}
}

/**
* Check if this user is a zone super administrator for the specified zone.
* @param Zone $zone to check for super administrator access
* @return bool true if user is zone super administrator
*/
public function is_zone_super_administrator(Zone $zone) {
return $this->access_to($zone) === 'zone-super-administrator';
}

/**
* List all zones that this user is an administrator of
* @param array $include list of extra data to include in response
Expand Down
22 changes: 21 additions & 1 deletion model/zone.php
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,22 @@ public function export_as_bind9_format() {
*/
public function add_pending_update($update) {
global $active_user;

// Security check: Prevent creation of pending updates for restricted record types
// Only global admins and zone super administrators can request changes to SOA, NS, and CAA records
$update_data = json_decode($update);
if($update_data && isset($update_data->actions)) {
foreach($update_data->actions as $action) {
if(($action->type == 'SOA' || $action->type == 'NS' || $action->type == 'CAA') ||
(isset($action->oldtype) && ($action->oldtype == 'SOA' || $action->oldtype == 'NS' || $action->oldtype == 'CAA'))) {
if(!($active_user->admin || $active_user->is_zone_super_administrator($this))) {
throw new RuntimeException('You are not authorized to request changes to SOA, NS, or CAA records.');
}
break;
}
}
}

$stmt = $this->database->prepare('INSERT INTO pending_update (zone_id, author_id, request_date, raw_data) VALUES (?, ?, NOW(), ?)');
$stmt->bindParam(1, $this->id, PDO::PARAM_INT);
$stmt->bindParam(2, $active_user->id, PDO::PARAM_INT);
Expand Down Expand Up @@ -781,7 +797,11 @@ private function process_rrset_action($update, &$trash, &$revs_missing, &$revs_u
$update->oldname = $update->name;
$update->oldtype = $update->type;
}
if(($update->type == 'SOA' || $update->type == 'NS') && !$active_user->admin) return;
// Security check: Only global admins and zone super administrators can edit SOA, NS and CAA records.
// We must check BOTH the new type and the original type to prevent privilege-escalation via rename/delete.
if((($update->type == 'SOA' || $update->type == 'NS' || $update->type == 'CAA') ||
(isset($update->oldtype) && ($update->oldtype == 'SOA' || $update->oldtype == 'NS' || $update->oldtype == 'CAA')))
&& !($active_user->admin || $active_user->is_zone_super_administrator($this))) return;

if(isset($config['dns']['autocreate_reverse_records'])) {
$autocreate_ptr = (bool)$config['dns']['autocreate_reverse_records'];
Expand Down
26 changes: 20 additions & 6 deletions public_html/extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ $(function() {
$('#new_ttl').data('default-value', $('#new_ttl').val());
$('option:first-of-type', typeselect).remove();
$('button.delete-rr', form).on('click', function() { delete_rr($(this)); });
$('td.name', form).on('click', function() { make_editable($(this), 'name'); });
$('td.type', form).on('click', function() { make_editable($(this), 'type'); });
$('td.ttl', form).on('click', function() { make_editable($(this), 'ttl'); });
$('td.content', form).on('click', function() { make_editable($(this), 'content'); });
$('td.enabled', form).on('click', function() { make_editable($(this)); });
$('td.comment', form).on('click', function() { make_editable($(this), 'comment'); });
$('td.name', form).on('click', function() { if(can_edit_record($(this))) make_editable($(this), 'name'); });
$('td.type', form).on('click', function() { if(can_edit_record($(this))) make_editable($(this), 'type'); });
$('td.ttl', form).on('click', function() { if(can_edit_record($(this))) make_editable($(this), 'ttl'); });
$('td.content', form).on('click', function() { if(can_edit_record($(this))) make_editable($(this), 'content'); });
$('td.enabled', form).on('click', function() { if(can_edit_record($(this))) make_editable($(this)); });
$('td.comment', form).on('click', function() { if(can_edit_record($(this))) make_editable($(this), 'comment'); });
$('tbody tr', form).each(function() { max_rrsetnum = Math.max(max_rrsetnum, parseInt($(this).data('rrsetnum'), 10)); });
$('#new_name').on('keyup', function(event) { validate_new(); if(event.which == 13) add_new(); });
$('#new_name').on('change', function() { validate_new(); });
Expand Down Expand Up @@ -117,6 +117,20 @@ $(function() {
update_changed(button);
}

// Check if user can edit this record
function can_edit_record(element) {
var tr = element.closest('tr');
var recordType = tr.data('type');
var userAdmin = form.data('user-admin') == 1;
var userSuperadmin = form.data('user-super-zone-admin') == 1;

// NS and CAA records can only be edited by admins or super zone admins
if((recordType == 'NS' || recordType == 'CAA') && !(userAdmin || userSuperadmin)) {
return false;
}
return true;
}

// Make the selected row editable and focus the chosen cell
function make_editable(element, cell) {
// Get row details of this rrset
Expand Down
19 changes: 19 additions & 0 deletions public_html/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,22 @@ div.stickyHeader th {
.javascript .nu0 {color: #CC0000;}
.javascript .me1 {color: #660066;}
.javascript span.xtra { display:block; }

/* Read-only records styling */
table.rrsets tr.read-only {
opacity: 0.7;
}
table.rrsets tr.read-only td {
cursor: not-allowed;
}
table.rrsets tr.read-only td:hover {
background-color: #f5f5f5;
}

/* User info in navbar */
.navbar-text .glyphicon-user {
margin-right: 5px;
}
.navbar-text {
color: #777;
}
8 changes: 8 additions & 0 deletions templates/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
<?php } ?>
<?php } ?>
</ul>
<?php if(is_object($this->get('active_user'))) { ?>
<ul class="nav navbar-nav navbar-right">
<li class="navbar-text">
<span class="glyphicon glyphicon-user"></span>
<?php out($this->get('active_user')->name)?> (<?php out($this->get('active_user')->uid)?>)
</li>
</ul>
<?php } ?>
</div>
</div>
</div>
Expand Down
Loading