mirror of
https://github.com/moodle/moodle.git
synced 2025-06-04 07:06:45 +02:00
This commit includes the following changes: - Implemented a scheduled task to automate the download and update of the GeoIP database. - Integrated checksum verification for the downloaded database file. - Added new configuration settings to enable administrators to enter MaxMind account credentials, allowing authenticated access to both free and paid GeoIP database versions from Maxmind.
184 lines
7.6 KiB
PHP
184 lines
7.6 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace core\task;
|
|
|
|
use core\http_client;
|
|
use moodle_exception;
|
|
use PharData;
|
|
|
|
/**
|
|
* Simple task to update the GeoIP database file.
|
|
*
|
|
* @package core
|
|
* @author Trisha Milan <trishamilan@catalyst-au.net>
|
|
* @copyright Monash University 2024
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class update_geoip2file_task extends scheduled_task {
|
|
|
|
/**
|
|
* Get a descriptive name for this task (shown to admins).
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_name(): string {
|
|
return get_string('taskupdategeoip2file', 'admin');
|
|
}
|
|
|
|
/**
|
|
* Execute the task to update the GeoIP2 database file.
|
|
*
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
* @throws moodle_exception
|
|
*/
|
|
public function execute(): void {
|
|
global $CFG;
|
|
|
|
if (!$CFG->geoipmaxmindaccid || !$CFG->geoipmaxmindlicensekey) {
|
|
mtrace("MaxMind account information is incomplete. Please configure the account ID and license key.");
|
|
return;
|
|
}
|
|
|
|
// Setup base directory path and permissions.
|
|
$geoip2file = $CFG->geoip2file;
|
|
$geoipdirectory = dirname($geoip2file);
|
|
if (!check_dir_exists($geoipdirectory) && !mkdir($geoipdirectory, $CFG->directorypermissions, true)) {
|
|
throw new moodle_exception("Cannot create output directory $geoipdirectory");
|
|
}
|
|
|
|
$geoippermalink = 'https://download.maxmind.com/geoip/databases/' . $CFG->geoipdbedition . '/download';
|
|
|
|
$client = new http_client(['auth' => [$CFG->geoipmaxmindaccid, $CFG->geoipmaxmindlicensekey]]);
|
|
$response = $client->head($geoippermalink, ['query' => ['suffix' => 'tar.gz']]);
|
|
$headers = $response->getHeaders();
|
|
$lastmodified = strtotime($headers['Last-Modified'][0]);
|
|
if (!$this->is_update_needed($geoip2file, $lastmodified)) {
|
|
mtrace("No update needed. The GeoIP database is up to date.");
|
|
return;
|
|
}
|
|
|
|
// Define path for downloading the GeoIP2 archive.
|
|
$archivefilename = 'GeoIP-City.tar.gz';
|
|
$tempdirectory = make_request_directory(true);
|
|
$geoipdownloadpath = $tempdirectory . '/' . $archivefilename;
|
|
|
|
mtrace("Downloading $CFG->geoipdbedition database from MaxMind......");
|
|
$response = $client->request('GET', $geoippermalink, [
|
|
'query' => ['suffix' => 'tar.gz'],
|
|
'sink' => $geoipdownloadpath,
|
|
]);
|
|
if ($response->getStatusCode() != 200) {
|
|
throw new moodle_exception("Error downloading file.");
|
|
}
|
|
|
|
mtrace("File downloaded successfully to $geoipdownloadpath");
|
|
mtrace("Verifying checksum......");
|
|
|
|
// Get the latest checksum from MaxMind.
|
|
$checksumcontent = $client->get($geoippermalink, ['query' => ['suffix' => 'tar.gz.sha256']])->getBody()->getContents();
|
|
list($checksum) = explode(' ', $checksumcontent);
|
|
if (!$this->verify_checksum($checksum, $geoipdownloadpath)) {
|
|
throw new moodle_exception("Checksum verification failed.");
|
|
}
|
|
|
|
mtrace("Checksum verified successfully.");
|
|
if ($this->update_geoip2file($geoipdownloadpath, $tempdirectory, $geoip2file)) {
|
|
// Store the last seen timestamp.
|
|
set_config('geoip_last_seen_timestamp', $lastmodified);
|
|
mtrace("GeoIP database update successful!");
|
|
} else {
|
|
throw new moodle_exception("GeoIP database update failed.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if an update is needed for the GeoIP2 file based on the last modified date.
|
|
*
|
|
* @param string $geoip2file The path to the GeoIP2 file that needs to be checked for updates.
|
|
* @param string $lastmodified The last modified date to be compared against the stored last seen timestamp.
|
|
* @return bool
|
|
*/
|
|
private function is_update_needed(string $geoip2file, string $lastmodified): bool {
|
|
return !file_exists($geoip2file) || $lastmodified !== get_config('core', 'geoip_last_seen_timestamp');
|
|
}
|
|
|
|
/**
|
|
* Verify the checksum of the downloaded file against an expected checksum.
|
|
*
|
|
* @param string $expectedchecksum The checksum expected for the file.
|
|
* @param string $geoipdownloadpath The path where the downloaded geoip archive is located.
|
|
* @return bool Returns true if the checksums match, returns false otherwise.
|
|
*/
|
|
private function verify_checksum(string $expectedchecksum, string $geoipdownloadpath): bool {
|
|
$actualchecksum = hash_file('sha256', $geoipdownloadpath);
|
|
return $expectedchecksum === $actualchecksum;
|
|
}
|
|
|
|
/**
|
|
* Extract the archive and update the GeoIP2 database file.
|
|
*
|
|
* @param string $archivepath The path to the archive file that needs to be extracted.
|
|
* @param string $targetdirectory Directory where the archive contents will be extracted.
|
|
* @param string $geoip2file The path to move the extracted GeoIP2 file.
|
|
* @return bool Returns true if the file was successfully extracted and moved to the specified location,
|
|
* false if any part of the process fails.
|
|
*/
|
|
private function update_geoip2file(string $archivepath, string $targetdirectory, string $geoip2file): bool {
|
|
$archive = new PharData($archivepath);
|
|
$archivename = $archive->getFilename();
|
|
|
|
mtrace("Extracting file......");
|
|
$archive->extractTo($targetdirectory);
|
|
$sourcefolder = $targetdirectory . '/' . $archivename;
|
|
|
|
// Find the mmdb file.
|
|
$mmdbfiles = glob($sourcefolder . '/*.mmdb');
|
|
if (count($mmdbfiles) > 1) {
|
|
throw new moodle_exception("Multiple .mmdb files found in the extracted folder.");
|
|
} else if (count($mmdbfiles) === 0) {
|
|
throw new moodle_exception("GeoIP file does not exist.");
|
|
}
|
|
|
|
// Backup existing GeoIP file before attempting to update.
|
|
$geoip2filename = basename($geoip2file);
|
|
$backuppath = $targetdirectory . '/' . 'backup_' . $geoip2filename;
|
|
if (file_exists($geoip2file)) {
|
|
if (!rename($geoip2file, $backuppath)) {
|
|
mtrace("Failed to create a backup of the existing GeoIP database.");
|
|
}
|
|
mtrace("Temporary backup of existing GeoIP file has been created.");
|
|
}
|
|
|
|
mtrace("Moving {$mmdbfiles[0]} into $geoip2file");
|
|
if (!copy($mmdbfiles[0], $geoip2file)) {
|
|
mtrace("Failed to update $geoip2filename.");
|
|
// Attempt to restore the original file from the backup.
|
|
if (file_exists($backuppath)) {
|
|
mtrace("Attempting to restore from backup.");
|
|
if (!copy($backuppath, $geoip2file)) {
|
|
throw new moodle_exception("Failed to restore the GeoIP database from backup.");
|
|
} else {
|
|
mtrace("The GeoIP database has been restored from the backup successfully.");
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
mtrace("$geoip2filename updated successfully.");
|
|
return true;
|
|
}
|
|
}
|