Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow adding storage locations and setup permissions #642

Merged
merged 14 commits into from
Jan 16, 2024
2 changes: 2 additions & 0 deletions scripts/build_helper/post_make_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ mkdir ${DST_DIR}/etc/cron.d
cp ${SRC_PATH}/debian/bluecherry.cron ${DST_DIR}/etc/cron.d/bluecherry
cp -a ${SRC_PATH}/www ${DST_DIR}/usr/share/bluecherry
cp -a ${SRC_PATH}/misc/sudoers.d ${DST_DIR}/usr/share/bluecherry/
cp -a ${SRC_PATH}/scripts/check_dir_permission.sh ${DST_DIR}/usr/share/bluecherry/scripts/check_dir_permission.sh


echo "${VERSION}" > ${DST_DIR}/usr/share/bluecherry/version

Expand Down
98 changes: 77 additions & 21 deletions scripts/check_dir_permission.sh
Original file line number Diff line number Diff line change
@@ -1,30 +1,86 @@
#!/bin/bash

# script to change permission of file
# Script to change permissions and ownership of a directory, with enhanced safety checks

# File variable to store location
# List of protected directories
PROTECTED_DIRS=(
"/"
"/bin"
"/boot"
"/dev"
"/etc"
"/home"
"/lib"
"/lib64"
"/opt"
"/proc"
"/root"
"/run"
"/sbin"
"/srv"
"/sys"
"/tmp"
"/usr"
"/var"
)

FILE="$1"
# Verify if a directory is "protected"
is_subdir() {
local dir=$1
local protected_dir=$2
while [[ "$dir" != "/" ]]; do
if [[ "$dir" == "$protected_dir" ]]; then
return 0 # True, is a subdir
fi
dir=$(dirname "$dir")
done
return 1 # False, not a subdir
}

if [[ ! -e "${FILE}" ]]; then
# creating directory...
mkdir -p "${FILE}"
# write permission of other and group of file
chmod 770 "${FILE}"
chown -R bluecherry:bluecherry "${FILE}"
elif [[ ! -d "${FILE}" ]]; then
echo "FILE already exists but is not a directory"
fi
# Directory variable to store location
DIR=$(realpath "$1") # Converts to absolute path

# find out if file has write permission or not
#[ -w $FILE ] && W="Write = yes" || W="Write = No"
[ -w $FILE ] && W=w || W='-'
# We need to atleast allow storage for the default storage directory which is inside a protected directory /var/lib/bluecherry/recordings
if [[ "$DIR" == "/var/lib/bluecherry/recordings" ]]; then
echo "Modifying permissions and ownership of $DIR"
chmod 770 "$DIR"
chown -R bluecherry:bluecherry "$DIR"
exit 0
fi

# find out if file has excute permission or not
[ -x $FILE ] && X=x || X='-'
# Check if the directory is a subdirectory of any protected directory
for protected_dir in "${PROTECTED_DIRS[@]}"; do
if is_subdir "$DIR" "$protected_dir"; then
# Allow /var/lib/bluecherry/recordings but disallow others in /var
if [[ "$protected_dir" == "/var" && "$DIR" != "/var/lib/bluecherry/recordings" ]]; then
echo "Error: Attempted to modify restricted directory $DIR. Operation aborted."
exit 1
elif [[ "$protected_dir" != "/var" ]]; then
echo "Error: Attempted to modify directory $DIR inside protected path $protected_dir. Operation aborted."
exit 1
fi
fi
done

# find out if file has read permission or not
[ -r $FILE ] && R=r || R='-'
# Check if the directory exists
if [[ -d "$DIR" ]]; then
# Check the permissions and ownership
PERMS=$(stat -c "%a" "$DIR")
OWNER=$(stat -c "%U.%G" "$DIR")

#echo "$FILE permissions"
echo "-$W$R$X"
# Check if permissions are not 770 or owner is not bluecherry.bluecherry
if [[ "$PERMS" != "770" || "$OWNER" != "bluecherry.bluecherry" ]]; then
echo "Changing permissions and ownership of $DIR"
chmod 770 "$DIR"
chown -R bluecherry:bluecherry "$DIR"
else
echo "Directory $DIR already has the correct permissions and ownership."
fi
elif [[ -e "$DIR" ]]; then
echo "$DIR already exists but is not a directory."
else
echo "Directory $DIR does not exist. Creating it now..."
mkdir -p "$DIR"
chmod 770 "$DIR"
chown -R bluecherry:bluecherry "$DIR"
fi
23 changes: 13 additions & 10 deletions www/ajax/storage.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?php
<?php

class storage extends Controller {

public function __construct(){
parent::__construct();
$this->chAccess('admin');
$this->chAccess('admin');
}

public function getData()
{
$this->setView('ajax.storage');

$locations = data::query("SELECT * FROM Storage");
$locations = data::query("SELECT * FROM Storage");
foreach(array_keys($locations) as $id) {
$path = database::escapeString($locations[$id]['path']);
$max_thresh = database::escapeString($locations[$id]['max_thresh']);
Expand All @@ -22,9 +22,9 @@ public function getData()
"SELECT SUM(CASE WHEN end < start THEN 0 ELSE end - start END".
") period, SUM(size) DIV 1048576 size".
" FROM Media WHERE filepath LIKE '{$path}%') q";
$result = data::query($query);
$locations[$id]['record_time'] = $result[0]['result'];
$locations[$id]['used_percent'] = ceil((disk_total_space($path) - disk_free_space($path)) / disk_total_space($path) * 100);
$result = data::query($query);
$locations[$id]['record_time'] = $result[0]['result'];
$locations[$id]['used_percent'] = ceil((disk_total_space($path) - disk_free_space($path)) / disk_total_space($path) * 100);
}

$this->view->locations = $locations;
Expand All @@ -36,8 +36,12 @@ public function postData()

$values = Array();
foreach($_POST['path'] as $key => $path){
$dir_status = $storage_check->directory_status($path);
if ($dir_status) return data::responseJSON($dir_status[0], $dir_status[1]);
// Call the updated method from storagecheck
$dir_status = $storage_check->change_directory_permissions($path);
if ($dir_status[0] !== 'OK') {
// If directory permissions change failed, return the error
return data::responseJSON($dir_status[0], $dir_status[1]);
}

$max = intval($_POST['max'][$key]);
$min = $max - 10;
Expand All @@ -55,4 +59,3 @@ public function postData()
data::responseJSON($status);
}
}

69 changes: 26 additions & 43 deletions www/ajax/storagecheck.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<?php

<?php

class storagecheck extends Controller {

public function __construct()
{
parent::__construct();
$this->chAccess('admin');
$this->chAccess('admin');
}

public function getData()
Expand All @@ -21,47 +20,31 @@ public function postData()

private function initProc()
{
$check_dir = $this->directory_status($_GET['path'], 'new');

data::responseJSON($check_dir[0], $check_dir[1]);
$change_status = $this->change_directory_permissions($_GET['path']);
data::responseJSON($change_status[0], $change_status[1]);
}

// path -- path to storage dir, type -- new or existing, existing are not checked for existing files
public function directory_status($path, $type = '')
{
$dir = shell_exec("sudo /usr/share/bluecherry/scripts/check_dir_permission.sh $path");

if (!file_exists($path)){
return array('F', str_replace('%PATH%', $path, DIR_DOES_NOT_EXIST_OR_NOT_READABLE));
}

if(!substr_count($dir, '-wrx')){
return array('F', str_replace('%PATH%', $path, DIR_NOT_WRITABLE));
}
$file_group = posix_getgrgid(filegroup($path));
$allowed_group = array('bluecherry', 'www-data');
if ((!isset($file_group['name'])) || (isset($file_group['name']) && (!in_array($file_group['name'], $allowed_group)))) {
return array('F', str_replace('%PATH%', $path, DIR_NOT_READABLE));
}

$file_owner = posix_getpwuid(fileowner($path));
if ((!isset($file_owner['name'])) || (isset($file_owner['name']) && (!in_array($file_group['name'], $allowed_group)))) {
return array('F', str_replace('%PATH%', $path, DIR_NOT_READABLE));
}

if (!is_writable($path)) {
return array('F', str_replace('%PATH%', $path, DIR_NOT_WRITABLE));
}

if (!is_readable($path)) {
return array('F', str_replace('%PATH%', $path, DIR_NOT_READABLE));
}

if ($type == 'new'){
return (count(scandir($path)) == 2) ? array('OK', DIR_OK) : array('INFO', str_replace('%PATH%', $path, DIR_NOT_EMPTY));
}

return false;
// Function to call the bash script to change permissions of a directory

public function change_directory_permissions($path)
{
// Call the bash script to change directory permissions and ownership
$output = shell_exec("sudo /usr/share/bluecherry/scripts/check_dir_permission.sh " . escapeshellarg($path));

// Interpret the output from the script to form the response
if (strpos($output, 'Error') !== false) {
return array('F', $output);
} elseif (strpos($output, 'Changing permissions') !== false || strpos($output, 'Modifying permissions and ownership') !== false) {
return array('OK', 'Permissions and ownership changed for ' . $path);
} elseif (strpos($output, 'already has the correct permissions and ownership') !== false) {
return array('OK', '');
} elseif (strpos($output, 'does not exist. Creating it now') !== false) {
// Handle the case where the directory is being created
return array('OK', 'Directory created and permissions set for ' . $path);
} else {
return array('F', 'Unexpected output from shell script: ' . $output);
}
curtishall marked this conversation as resolved.
Show resolved Hide resolved
}


}
2 changes: 1 addition & 1 deletion www/lib/lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@
define('MJPEG_DEVICE_ID_NOT_SUPPLIED', 'No device ID supplied.');

#storage
define('STRAGE_HEADER', 'Manage storage locations');
define('STORAGE_HEADER', 'Manage storage locations');
define('ADD_LOCATION', 'Add location');
define('LOCATION', 'Folder:');
define('STORAGE_INFO_MESSAGE', 'Please note that if you add a new storage location, you need to make sure that: <br /> - folder exists <br /> - folder is empty <br /> - folder belongs to user bluecherry, group bluecherry.');
Expand Down
Loading
Loading