MDL-60365 tool_mobile: Extra configuration checks
authorJuan Leyva <juanleyvadelgado@gmail.com>
Fri, 6 Oct 2017 10:24:53 +0000 (12:24 +0200)
committerJuan Leyva <juanleyvadelgado@gmail.com>
Thu, 12 Oct 2017 09:46:18 +0000 (11:46 +0200)
admin/tool/mobile/classes/api.php
admin/tool/mobile/lang/en/tool_mobile.php
admin/tool/mobile/tests/api_test.php
lib/adminlib.php

index 7d1674a..bcc26d7 100644 (file)
@@ -30,6 +30,7 @@ use context_system;
 use moodle_url;
 use moodle_exception;
 use lang_string;
+use curl;
 
 /**
  * API exposed by tool_mobile, to be used mostly by external functions and the plugin settings.
@@ -356,4 +357,86 @@ class api {
 
         return $features;
     }
+
+    /**
+     * This function check the current site for potential configuration issues that may prevent the mobile app to work.
+     *
+     * @return array list of potential issues
+     * @since  Moodle 3.4
+     */
+    public static function get_potential_config_issues() {
+        global $CFG;
+        require_once($CFG->dirroot . "/lib/filelib.php");
+        require_once($CFG->dirroot . '/message/lib.php');
+
+        $warnings = array();
+
+        $curl = new curl();
+        // Return certificate information and verify the certificate.
+        $curl->setopt(array('CURLOPT_CERTINFO' => 1, 'CURLOPT_SSL_VERIFYPEER' => true));
+        $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); // Force https url.
+        $curl->head($httpswwwroot . "/login/index.php");
+        $info = $curl->get_info();
+
+        // First of all, check the server certificate (if any).
+        if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
+            $warnings[] = ['nohttpsformobilewarning', 'admin'];
+        } else {
+            // Check the certificate is not self-signed or has an untrusted-root.
+            // This may be weak in some scenarios (when the curl SSL verifier is outdated).
+            if (empty($info['certinfo'])) {
+                $warnings[] = ['selfsignedoruntrustedcertificatewarning', 'tool_mobile'];
+            } else {
+                $timenow = time();
+                $expectedissuer = null;
+                foreach ($info['certinfo'] as $cert) {
+                    // Check if the signature algorithm is weak (Android won't work with SHA-1).
+                    if ($cert['Signature Algorithm'] == 'sha1WithRSAEncryption' || $cert['Signature Algorithm'] == 'sha1WithRSA') {
+                        $warnings[] = ['insecurealgorithmwarning', 'tool_mobile'];
+                    }
+                    // Check certificate start date.
+                    if (strtotime($cert['Start date']) > $timenow) {
+                        $warnings[] = ['invalidcertificatestartdatewarning', 'tool_mobile'];
+                    }
+                    // Check certificate end date.
+                    if (strtotime($cert['Expire date']) < $timenow) {
+                        $warnings[] = ['invalidcertificateexpiredatewarning', 'tool_mobile'];
+                    }
+                    // Check the chain.
+                    if ($expectedissuer !== null) {
+                        if ($expectedissuer !== $cert['Subject'] || $cert['Subject'] === $cert['Issuer']) {
+                            $warnings[] = ['invalidcertificatechainwarning', 'tool_mobile'];
+                        }
+                    }
+                    $expectedissuer = $cert['Issuer'];
+                }
+            }
+        }
+        // Now check typical configuration problems.
+        if ((int) $CFG->userquota === PHP_INT_MAX) {
+            // In old Moodle version was a text so was possible to have numeric values > PHP_INT_MAX.
+            $warnings[] = ['invaliduserquotawarning', 'tool_mobile'];
+        }
+        // Check ADOdb debug enabled.
+        if (get_config('auth_db', 'debugauthdb') || get_config('enrol_database', 'debugdb')) {
+            $warnings[] = ['adodbdebugwarning', 'tool_mobile'];
+        }
+        // Check display errors on.
+        if (!empty($CFG->debugdisplay)) {
+            $warnings[] = ['displayerrorswarning', 'tool_mobile'];
+        }
+        // Check mobile notifications.
+        $processors = get_message_processors();
+        $enabled = false;
+        foreach ($processors as $processor => $status) {
+            if ($processor == 'airnotifier' && $status->enabled) {
+                $enabled = true;
+            }
+        }
+        if (!$enabled) {
+            $warnings[] = ['mobilenotificationsdisabledwarning', 'tool_mobile'];
+        }
+
+        return $warnings;
+    }
 }
index c8b207c..d82695a 100644 (file)
@@ -22,6 +22,7 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['adodbdebugwarning'] = 'ADOdb debugging is enabled. It should be disabled in the external database authentication or external database enrolment plugin settings.';
 $string['androidappid'] = 'Android app\'s unique identifier';
 $string['androidappid_desc'] = 'This setting may be left as default unless you have a custom Android app.';
 $string['autologinkeygenerationlockout'] = 'Auto-login key generation is blocked because of too many requests within an hour.';
@@ -49,6 +50,7 @@ Mis calificaciones|https://someurl.xyz/local/mygrades/index.php|embedded|es
 </pre>';
 $string['disabledfeatures'] = 'Disabled features';
 $string['disabledfeatures_desc'] = 'Select here the features you want to disable in the Mobile app for your site. Please note that some features listed here could be already disabled via other site settings. You will have to log out and log in again in the app to see the changes.';
+$string['displayerrorswarning'] = 'Display debug messages (debugdisplay) is enabled. It should be disabled.';
 $string['enablesmartappbanners'] = 'Enable App Banners';
 $string['enablesmartappbanners_desc'] = 'If enabled, a banner promoting the mobile app will be displayed when accessing the site using a mobile browser.';
 $string['forcedurlscheme'] = 'If you want to allow only your custom branded app to be opened via a browser window, then specify its URL scheme here; otherwise leave the field empty.';
@@ -56,7 +58,12 @@ $string['forcedurlscheme_key'] = 'URL scheme';
 $string['forcelogout'] = 'Force log out';
 $string['forcelogout_desc'] = 'If enabled, the app option \'Change site\' is replaced by \'Log out\'. This results in the user being completely logged out. They must then re-enter their password the next time they wish to access the site.';
 $string['httpsrequired'] = 'HTTPS required';
+$string['insecurealgorithmwarning'] = 'It seems that the HTTPS certificate uses an insecure algorithm for signing (SHA-1). Please try updating the certificate.';
+$string['invalidcertificatechainwarning'] = 'It seems that the certificate chain is invalid.';
+$string['invalidcertificateexpiredatewarning'] = 'It seems that the HTTPS certificate for the site has expired.';
+$string['invalidcertificatestartdatewarning'] = 'It seems that the HTTPS certificate for the site is not yet valid (with a start date in the future).';
 $string['invalidprivatetoken'] = 'Invalid private token. Token should not be empty or passed via GET parameter.';
+$string['invaliduserquotawarning'] = 'The user quota (userquota) is set to an invalid number. It should be set to a valid number (an integer value) in Site policies.';
 $string['iosappid'] = 'iOS app\'s unique identifier';
 $string['iosappid_desc'] = 'This setting may be left as default unless you have a custom iOS app.';
 $string['loginintheapp'] = 'Via the app';
@@ -70,8 +77,10 @@ $string['mobileappearance'] = 'Mobile appearance';
 $string['mobileauthentication'] = 'Mobile authentication';
 $string['mobilecssurl'] = 'CSS';
 $string['mobilefeatures'] = 'Mobile features';
+$string['mobilenotificationsdisabledwarning'] = 'Mobile notifications are not enabled. They should be enabled in Manage message outputs.';
 $string['mobilesettings'] = 'Mobile settings';
 $string['pluginname'] = 'Moodle Mobile tools';
+$string['selfsignedoruntrustedcertificatewarning'] = 'It seems that the HTTPS certificate is self-signed or not trusted. The mobile app will only work with trusted sites.';
 $string['setuplink'] = 'App download page';
 $string['setuplink_desc'] = 'URL of page with links to download the mobile app from the App Store and Google Play.';
 $string['smartappbanners'] = 'App Banners';
index ece75c4..f5ee434 100644 (file)
@@ -63,4 +63,35 @@ class tool_mobile_api_testcase extends externallib_advanced_testcase {
         $this->assertTimeCurrent($key->validuntil - api::LOGIN_KEY_TTL);
         $this->assertEquals('0.0.0.0', $key->iprestriction);
     }
+
+    /**
+     * Test get_potential_config_issues.
+     */
+    public function test_get_potential_config_issues() {
+        global $CFG;
+        require_once($CFG->dirroot . '/message/lib.php');
+
+        $this->resetAfterTest(true);
+        $this->setAdminUser();
+
+        $CFG->userquota = '73289234723498234723423489273423497234234';
+        $CFG->debugdisplay = 1;
+        set_config('debugauthdb', 1, 'auth_db');
+        set_config('debugdb', 1, 'enrol_database');
+        $expectedissues = array('nohttpsformobilewarning', 'invaliduserquotawarning', 'adodbdebugwarning', 'displayerrorswarning',
+            'mobilenotificationsdisabledwarning');
+
+        $processors = get_message_processors();
+        foreach ($processors as $processor => $status) {
+            if ($processor == 'airnotifier' && $status->enabled) {
+                unset($expectedissues['mobilenotificationsdisabledwarning']);
+            }
+        }
+
+        $issues = api::get_potential_config_issues();
+        $this->assertCount(count($expectedissues), $issues);
+        foreach ($issues as $issue) {
+            $this->assertTrue(in_array($issue[0], $expectedissues));
+        }
+    }
 }
index 903da8d..520f7ba 100644 (file)
@@ -8746,17 +8746,14 @@ class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
      * @return string XHTML
      */
     public function output_html($data, $query='') {
-        global $CFG, $OUTPUT;
+        global $OUTPUT;
         $html = parent::output_html($data, $query);
 
         if ((string)$data === $this->yes) {
-            require_once($CFG->dirroot . "/lib/filelib.php");
-            $curl = new curl();
-            $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); //force https url
-            $curl->head($httpswwwroot . "/login/index.php");
-            $info = $curl->get_info();
-            if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
-               $html .= $OUTPUT->notification(get_string('nohttpsformobilewarning', 'admin'));
+            $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
+            foreach ($notifications as $notification) {
+                $message = get_string($notification[0], $notification[1]);
+                $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
             }
         }