diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1cbec1b83..95a222554 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -5,7 +5,7 @@ FROM ubuntu:noble # See the documentation here to see why devcontainers are awesome: # https://code.visualstudio.com/docs/remote/containers -ARG PHP_VERSION="5.6" +ARG PHP_VERSION="7.4" # Install a bunch of stuff from the standard repositories and a custom PHP repository RUN apt-get update && apt-get install -y software-properties-common && \ diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 75d018548..76ae251fa 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: app: - image: ghcr.io/c4g/blis-devcontainer:latest + image: ghcr.io/c4g/blis-devcontainer:unstable environment: - BLIS_LAB_BACKUPS_V2_ENABLED=1 - BLIS_LAB_CONNECTION_ENABLED=1 diff --git a/.editorconfig b/.editorconfig index bb53136e5..6a60b6272 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,4 +9,7 @@ indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true -insert_final_newline = true \ No newline at end of file +insert_final_newline = true + +[*.blis] +insert_final_newline = false diff --git a/.vscode/settings.json b/.vscode/settings.json index 1c525b972..d70165f59 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { // Uncomment if you are working with BLIS on Windows // "php.validate.executablePath": "server/php/php.exe" + "php.suggest.basic": false } diff --git a/Dockerfile b/Dockerfile index 42909dca3..f951e9c2b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:noble -ARG PHP_VERSION="5.6" +ARG PHP_VERSION="7.4" # Install a bunch of stuff from the standard repositories RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ diff --git a/bin/crypto.php b/bin/crypto.php new file mode 100755 index 000000000..71422c853 --- /dev/null +++ b/bin/crypto.php @@ -0,0 +1,45 @@ +#!/usr/bin/env php +info("Encryption succeeded."); + } +} + +if ($mode == "decrypt") { + $input = $argv[2]; + $output = $argv[3]; + $keyfile = $argv[4]; + + $result = Encryption::decryptFile($input, $output, $keyfile); + + if ($result) { + $log->info("Decryption succeeded."); + } +} + +if ($mode == "gen") { + $filename = $argv[2]; + + $key = sodium_crypto_box_keypair(); + file_put_contents($filename, base64_encode($key)); + + $pubkey = sodium_crypto_box_publickey($key); + file_put_contents($filename . ".pub", base64_encode($pubkey)); +} diff --git a/composer.json b/composer.json index 8e5f4d13a..a58bdcd20 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "require": { - "monolog/monolog": "^1.25", + "monolog/monolog": "< 3.0.0", "phpoffice/phpexcel": "= 1.8.2" } } diff --git a/composer.lock b/composer.lock index 0fb86e908..22bf14279 100644 --- a/composer.lock +++ b/composer.lock @@ -4,55 +4,71 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "28354d1d13e43da729e1e7a74044f5bc", + "content-hash": "364f7af6db1e807cc85ac9e31e5d7edf", "packages": [ { "name": "monolog/monolog", - "version": "1.27.1", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "904713c5929655dc9b97288b69cfeedad610c9a1" + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/904713c5929655dc9b97288b69cfeedad610c9a1", - "reference": "904713c5929655dc9b97288b69cfeedad610c9a1", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0" + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" }, "require-dev": { "aws/aws-sdk-php": "^2.4.9 || ^3.0", "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpstan/phpstan": "^0.12.59", - "phpunit/phpunit": "~4.5", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.38 || ^9.6.19", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, "autoload": { "psr-4": { "Monolog\\": "src/Monolog" @@ -66,11 +82,11 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "homepage": "https://github.com/Seldaek/monolog", "keywords": [ "log", "logging", @@ -78,7 +94,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/1.27.1" + "source": "https://github.com/Seldaek/monolog/tree/2.10.0" }, "funding": [ { @@ -90,7 +106,7 @@ "type": "tidelift" } ], - "time": "2022-06-09T08:53:42+00:00" + "time": "2024-11-12T12:43:37+00:00" }, { "name": "phpoffice/phpexcel", diff --git a/crypttest/README.md b/crypttest/README.md new file mode 100644 index 000000000..ea4ce91ff Binary files /dev/null and b/crypttest/README.md differ diff --git a/crypttest/readme_dec.md b/crypttest/readme_dec.md new file mode 100644 index 000000000..e47fdaa61 --- /dev/null +++ b/crypttest/readme_dec.md @@ -0,0 +1,18 @@ +BLIS +==== + +C4G Basic Laboratory Information System + +#### How to run BLIS +Clone the repository onto your machine. Download the BLIS runtime files from: [http://blis.cc.gatech.edu/files/BLISRuntime.zip] + +Unzip all files from BLISRuntime.zip into the the BLIS/ directory in your repository clone. +Run BLIS.exe to start BLIS. + + +#### Documentation webpage + +We are hosting online documentations (most updated version) via github page: [https://c4g.github.io/BLIS/](https://c4g.github.io/BLIS/). You can access those files via following links: +- [Frequent Asked Questions](https://c4g.github.io/BLIS/faq/) +- [User Guide](https://c4g.github.io/BLIS/) +- [Developer Documentation](https://c4g.github.io/BLIS/developer_documentation/developer_guide_v0.1/) \ No newline at end of file diff --git a/crypttest/receiver.key b/crypttest/receiver.key new file mode 100644 index 000000000..d00f01c1f --- /dev/null +++ b/crypttest/receiver.key @@ -0,0 +1 @@ +FF7XkxihsLYI+5Fu8vMjC9lCNBHyn2/4Jo7brvuNtzFNT24jLOaNnGL3x7HjcxoDcdW+j8x3agwCGE9tjUDgbQ== \ No newline at end of file diff --git a/crypttest/receiver.key.pub b/crypttest/receiver.key.pub new file mode 100644 index 000000000..a5d531d32 --- /dev/null +++ b/crypttest/receiver.key.pub @@ -0,0 +1 @@ +TU9uIyzmjZxi98ex43MaA3HVvo/Md2oMAhhPbY1A4G0= \ No newline at end of file diff --git a/crypttest/sender.key b/crypttest/sender.key new file mode 100644 index 000000000..20570160e --- /dev/null +++ b/crypttest/sender.key @@ -0,0 +1 @@ +GF5V8Eqlyu/BdrXEzOd5Fdlvk17UvKHkV+qG7K/+OKKmiaF1LMApshfNcF/aHa4KBq16UjJSGlj6GHsAzUb8Bw== \ No newline at end of file diff --git a/crypttest/sender.key.pub b/crypttest/sender.key.pub new file mode 100644 index 000000000..35057f755 --- /dev/null +++ b/crypttest/sender.key.pub @@ -0,0 +1 @@ +pomhdSzAKbIXzXBf2h2uCgatelIyUhpY+hh7AM1G/Ac= \ No newline at end of file diff --git a/crypttest/test.key b/crypttest/test.key new file mode 100644 index 000000000..9755be5bd --- /dev/null +++ b/crypttest/test.key @@ -0,0 +1 @@ +vLT09EPg/B9MC0CpRW55KvoaxGp7tm7LgEE05vjekK9ydDpIxnbIT161/szlDOE5eBJMIeok9j9OXOL5ApvKLQ== \ No newline at end of file diff --git a/crypttest/test.key.pub b/crypttest/test.key.pub new file mode 100644 index 000000000..68e70c2f9 --- /dev/null +++ b/crypttest/test.key.pub @@ -0,0 +1 @@ +cnQ6SMZ2yE9etf7M5QzhOXgSTCHqJPY/Tlzi+QKbyi0= \ No newline at end of file diff --git a/db/migrations/lab/20250915025855_add_keys_table.sql b/db/migrations/lab/20250915025855_add_keys_table.sql new file mode 100644 index 000000000..97f379271 --- /dev/null +++ b/db/migrations/lab/20250915025855_add_keys_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS `keys` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `type` varchar(100) NOT NULL, + `data` varchar(100) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +); diff --git a/db/migrations/revamp/20250916133257_add_enc_option_to_labconfig.sql b/db/migrations/revamp/20250916133257_add_enc_option_to_labconfig.sql new file mode 100644 index 000000000..7794a19ae --- /dev/null +++ b/db/migrations/revamp/20250916133257_add_enc_option_to_labconfig.sql @@ -0,0 +1,3 @@ +ALTER TABLE `lab_config` ADD COLUMN `backup_encryption_enabled` TINYINT(1) NOT NULL DEFAULT 0; + +ALTER TABLE `lab_config` ADD COLUMN `backup_encryption_key_id` int(11) unsigned DEFAULT NULL; diff --git a/htdocs/config/lab_config_add.php b/htdocs/config/lab_config_add.php index 5581e1a97..24b3632ea 100755 --- a/htdocs/config/lab_config_add.php +++ b/htdocs/config/lab_config_add.php @@ -219,7 +219,7 @@ # Copy contents from langdata_revamp into this new folder if (is_dir($LOCAL_PATH."/langdata_".$lab_config_id)) { - $log->warn("$LOCAL_PATH/langdata_$lab_config_id already exists. Deleting it."); + $log->warning("$LOCAL_PATH/langdata_$lab_config_id already exists. Deleting it."); PlatformLib::removeDirectory($LOCAL_PATH."/langdata_".$lab_config_id); } chmod($LOCAL_PATH."/langdata_revamp", 0755); diff --git a/htdocs/config/lab_config_resolver.php b/htdocs/config/lab_config_resolver.php index 0e2d0cf33..52d7d2006 100644 --- a/htdocs/config/lab_config_resolver.php +++ b/htdocs/config/lab_config_resolver.php @@ -49,6 +49,6 @@ public static function resolveId() { } } - $log->warn("Could not resolve lab_config_id. Logged in user ID: " . $_SESSION["user_id"]); + $log->warning("Could not resolve lab_config_id. Logged in user ID: " . $_SESSION["user_id"]); } } diff --git a/htdocs/config/v2/blis_cloud_server.php b/htdocs/config/v2/blis_cloud_server.php index 74ea26ecb..c54b240b9 100644 --- a/htdocs/config/v2/blis_cloud_server.php +++ b/htdocs/config/v2/blis_cloud_server.php @@ -34,20 +34,20 @@ $connection_code = str_replace("-", "", $_POST["connection_code"]); if ($action == "connect") { - $log->warn("Connection request received for lab $lab_config_id: $lab_name"); + $log->warning("Connection request received for lab $lab_config_id: $lab_name"); // look up connection $connection = LabConnection::find_by_lab_config_id($lab_config_id); if ($connection != null) { // Lab connection already exists, so this request is trying to re-connect. - $log->warn("Lab $lab_config_id is already connected. Re-connecting."); + $log->warning("Lab $lab_config_id is already connected. Re-connecting."); } $nrml_code = str_replace("-", "", $connection->connection_code); if ($connection_code != $nrml_code) { - $log->warn("Connection attempted with wrong connection code. Lab ID: $lab_config_id; Incorrect code: $connection_code"); + $log->warning("Connection attempted with wrong connection code. Lab ID: $lab_config_id; Incorrect code: $connection_code"); // Connection code does not match header(LangUtil::$generalTerms['404_BAD_REQUEST'], true, 400); exit; diff --git a/htdocs/config/v2/connect_to_blis_cloud.php b/htdocs/config/v2/connect_to_blis_cloud.php index 8eb102972..29e49a950 100644 --- a/htdocs/config/v2/connect_to_blis_cloud.php +++ b/htdocs/config/v2/connect_to_blis_cloud.php @@ -109,7 +109,7 @@ } else { $outstr = "Output: $output"; } - $log->warn("Request failed. Curl exit code: $return_code. $outstr"); + $log->warning("Request failed. Curl exit code: $return_code. $outstr"); $failure = true; } } diff --git a/htdocs/config/v2/lab_config_backup_create_keypair.php b/htdocs/config/v2/lab_config_backup_create_keypair.php new file mode 100644 index 000000000..58c97d6a9 --- /dev/null +++ b/htdocs/config/v2/lab_config_backup_create_keypair.php @@ -0,0 +1,75 @@ +labConfigId && is_admin($current_user)) { + $unauthorized = false; + } +} + +if ($unauthorized) { + header(LangUtil::$generalTerms['401_UNAUTHORIZE'], true, 401); + header("Location: /home.php"); + exit; +} + +$keypair_name = trim($_POST['keypair_name']); +if ($keypair_name == NULL || $keypair_name == "") { + $_SESSION['FLASH'] = "Must specify a name for the keypair."; + header("Bad Request", true, 400); + header("Location: lab_config_backup_settings.php?id=$lab_config_id"); + exit; +} + +$log->info("Generating new keypair for $lab_config_name ($lab_config_id)"); + +$key = sodium_crypto_box_keypair(); +$key_b64 = base64_encode($key); +sodium_memzero($key); + +try { + db_change($lab["db_name"]); + $db_key_id = Key::insert($keypair_name, Key::$KEYPAIR, $key_b64); + sodium_memzero($key_b64); +} catch (Exception $e) { + $_SESSION['FLASH'] = "An error occurred generating the keypair: " . $e->getMessage(); + header("Internal Server Error", true, 500); + header("Location: lab_config_backup_settings.php?id=$lab_config_id"); + exit; +} + +$_SESSION['FLASH'] = "Keypair generated successfully."; +header("Location: lab_config_backup_settings.php?id=$lab_config_id"); diff --git a/htdocs/config/v2/lab_config_backup_restore.php b/htdocs/config/v2/lab_config_backup_restore.php index 03a2e3c83..9dc716ee0 100644 --- a/htdocs/config/v2/lab_config_backup_restore.php +++ b/htdocs/config/v2/lab_config_backup_restore.php @@ -6,6 +6,7 @@ require_once(__DIR__."/../../users/accesslist.php"); require_once(__DIR__."/../../includes/composer.php"); +require_once(__DIR__."/../../includes/lab_config.php"); require_once(__DIR__."/../../includes/migrations.php"); require_once(__DIR__."/../../includes/user_lib.php"); require_once(__DIR__."/lib/backup.php"); @@ -17,8 +18,9 @@ $current_user = get_user_by_id($current_user_id); $lab_config_id = $_REQUEST['lab_config_id']; -$lab_db_name_query = "SELECT lab_config_id, name, db_name FROM lab_config WHERE lab_config_id = '$lab_config_id';"; +$lab_db_name_query = "SELECT * FROM lab_config WHERE lab_config_id = '$lab_config_id';"; $lab = query_associative_one($lab_db_name_query); +$lab_config = LabConfig::getObject($lab); db_change($lab['db_name']); $lab_config_name = $lab['name']; @@ -45,7 +47,7 @@ exit; } -$analyzed = $backup->analyze(); +$analyzed = $backup->analyze($lab_config->backup_encryption_key_id); if ($_GET["action"] != "confirm") { @@ -130,7 +132,7 @@ $start_time = microtime(true); $end_time = null; - $restorer = new BackupRestorer($backup, $lab_config_id); + $restorer = new BackupRestorer($backup, $lab_config_id, $lab_config->backup_encryption_key_id); $restore_successful = $restorer->restore(); diff --git a/htdocs/config/v2/lab_config_backup_settings.php b/htdocs/config/v2/lab_config_backup_settings.php index bbf74cc6a..9c1e9f3c2 100644 --- a/htdocs/config/v2/lab_config_backup_settings.php +++ b/htdocs/config/v2/lab_config_backup_settings.php @@ -7,6 +7,7 @@ # require_once(__DIR__."/../../users/accesslist.php"); +require_once(__DIR__."/../../includes/lab_config.php"); require_once(__DIR__."/../../includes/user_lib.php"); require_once(__DIR__."/lib/backup.php"); @@ -16,8 +17,10 @@ DbUtil::switchToGlobal(); -$lab_db_name_query = "SELECT lab_config_id, name, db_name FROM lab_config WHERE lab_config_id = '$lab_config_id';"; +$lab_db_name_query = "SELECT * FROM lab_config WHERE lab_config_id = '$lab_config_id';"; $lab = query_associative_one($lab_db_name_query); +$lab_config = LabConfig::getObject($lab); + db_change($lab['db_name']); $lab_config_name = $lab["name"]; @@ -47,44 +50,56 @@ require_once(__DIR__."/../../includes/header.php"); LangUtil::setPageId("lab_config_home"); -require_once(__DIR__."/../../includes/keymgmt.php"); - -// TODO: switch this to its own table, maybe... -$settings_encryption_enabled = KeyMgmt::read_enc_setting() != "0"; - -?> +$settings_encryption_enabled = $lab_config->backup_encryption_enabled; - +require_once(__DIR__."/../../encryption/keys.php"); + +function key_to_row($key) { + global $lab_config_id; + $key_id = $key->id; + echo( + "