]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Prepare settings for the API in the admin page and during the install
authorArthurHoaro <arthur@hoa.ro>
Sun, 31 Jul 2016 08:46:17 +0000 (10:46 +0200)
committerArthurHoaro <arthur@hoa.ro>
Mon, 12 Dec 2016 02:54:10 +0000 (03:54 +0100)
API settings:
   - api.enabled
   - api.secret

The API settings will be initialized (and the secret generated) with an update method.

application/Updater.php
application/Utils.php
index.php
tests/Updater/UpdaterTest.php
tests/UtilsTest.php
tpl/configure.html
tpl/install.html

index f0d02814b5599654b0c6638e40e02e86068a5ab7..38de33503a417ac06c9b186298c7621a471d3667 100644 (file)
@@ -256,6 +256,29 @@ class Updater
 
         return true;
     }
+
+    /**
+     * Initialize API settings:
+     *   - api.enabled: true
+     *   - api.secret: generated secret
+     */
+    public function updateMethodApiSettings()
+    {
+        if ($this->conf->exists('api.secret')) {
+            return true;
+        }
+
+        $this->conf->set('api.enabled', true);
+        $this->conf->set(
+            'api.secret',
+            generate_api_secret(
+                $this->conf->get('credentials.login'),
+                $this->conf->get('credentials.salt')
+            )
+        );
+        $this->conf->write($this->isLoggedIn);
+        return true;
+    }
 }
 
 /**
index 0a5b476ebf9779bbe47f8ea7f28ef0de21b28481..62902341aee1a3d2bea676c8bbe3a53a2a721984 100644 (file)
@@ -231,3 +231,29 @@ function autoLocale($headerLocale)
     }
     setlocale(LC_ALL, $attempts);
 }
+
+/**
+ * Generates a default API secret.
+ *
+ * Note that the random-ish methods used in this function are predictable,
+ * which makes them NOT suitable for crypto.
+ * BUT the random string is salted with the salt and hashed with the username.
+ * It makes the generated API secret secured enough for Shaarli.
+ *
+ * PHP 7 provides random_int(), designed for cryptography.
+ * More info: http://stackoverflow.com/questions/4356289/php-random-string-generator
+
+ * @param string $username Shaarli login username
+ * @param string $salt     Shaarli password hash salt
+ *
+ * @return string|bool Generated API secret, 12 char length.
+ *                     Or false if invalid parameters are provided (which will make the API unusable).
+ */
+function generate_api_secret($username, $salt)
+{
+    if (empty($username) || empty($salt)) {
+        return false;
+    }
+
+    return str_shuffle(substr(hash_hmac('sha512', uniqid($salt), $username), 10, 12));
+}
index cc4483524abd9e6dd8bb9dad96d98f764e48f007..25e37b32bed95d270260aa76ac7c9b4dcfadafc3 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1142,6 +1142,8 @@ function renderPage($conf, $pluginManager)
             $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks']));
             $conf->set('updates.check_updates', !empty($_POST['updateCheck']));
             $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks']));
+            $conf->set('api.enabled', !empty($_POST['apiEnabled']));
+            $conf->set('api.secret', escape($_POST['apiSecret']));
             try {
                 $conf->write(isLoggedIn());
             }
@@ -1170,6 +1172,8 @@ function renderPage($conf, $pluginManager)
             $PAGE->assign('enable_rss_permalinks', $conf->get('feed.rss_permalinks', false));
             $PAGE->assign('enable_update_check', $conf->get('updates.check_updates', true));
             $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false));
+            $PAGE->assign('api_enabled', $conf->get('api.enabled', true));
+            $PAGE->assign('api_secret', $conf->get('api.secret'));
             $PAGE->renderPage('configure');
             exit;
         }
@@ -1952,6 +1956,14 @@ function install($conf)
             $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER)));
         }
         $conf->set('updates.check_updates', !empty($_POST['updateCheck']));
+        $conf->set('api.enabled', !empty($_POST['enableApi']));
+        $conf->set(
+            'api.secret',
+            generate_api_secret(
+                $this->conf->get('credentials.login'),
+                $this->conf->get('credentials.salt')
+            )
+        );
         try {
             // Everything is ok, let's create config file.
             $conf->write(isLoggedIn());
index 4948fe52d20ec1e39ec30b5eb5f39633f70880d5..0171daada17ca4d424fc3ceb811c692b4da7d63c 100644 (file)
@@ -271,7 +271,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
     public function testEscapeConfig()
     {
         $sandbox = 'sandbox/config';
-        copy(self::$configFile .'.json.php', $sandbox .'.json.php');
+        copy(self::$configFile . '.json.php', $sandbox . '.json.php');
         $this->conf = new ConfigManager($sandbox);
         $title = '<script>alert("title");</script>';
         $headerLink = '<script>alert("header_link");</script>';
@@ -286,7 +286,43 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
         $this->assertEquals(escape($title), $this->conf->get('general.title'));
         $this->assertEquals(escape($headerLink), $this->conf->get('general.header_link'));
         $this->assertEquals(escape($redirectorUrl), $this->conf->get('redirector.url'));
-        unlink($sandbox .'.json.php');
+        unlink($sandbox . '.json.php');
+    }
+
+    /**
+     * Test updateMethodApiSettings(): create default settings for the API (enabled + secret).
+     */
+    public function testUpdateApiSettings()
+    {
+        $confFile = 'sandbox/config';
+        copy(self::$configFile .'.json.php', $confFile .'.json.php');
+        $conf = new ConfigManager($confFile);
+        $updater = new Updater(array(), array(), $conf, true);
+
+        $this->assertFalse($conf->exists('api.enabled'));
+        $this->assertFalse($conf->exists('api.secret'));
+        $updater->updateMethodApiSettings();
+        $conf->reload();
+        $this->assertTrue($conf->get('api.enabled'));
+        $this->assertTrue($conf->exists('api.secret'));
+        unlink($confFile .'.json.php');
+    }
+
+    /**
+     * Test updateMethodApiSettings(): already set, do nothing.
+     */
+    public function testUpdateApiSettingsNothingToDo()
+    {
+        $confFile = 'sandbox/config';
+        copy(self::$configFile .'.json.php', $confFile .'.json.php');
+        $conf = new ConfigManager($confFile);
+        $conf->set('api.enabled', false);
+        $conf->set('api.secret', '');
+        $updater = new Updater(array(), array(), $conf, true);
+        $updater->updateMethodApiSettings();
+        $this->assertFalse($conf->get('api.enabled'));
+        $this->assertEmpty($conf->get('api.secret'));
+        unlink($confFile .'.json.php');
     }
 
     /**
index 6a7870c4620ac5cd3bbf2a838f108c27755dd411..0cf9a921ed0ee979da42d0ec3d5846e822c919f7 100644 (file)
@@ -253,4 +253,21 @@ class UtilsTest extends PHPUnit_Framework_TestCase
             is_session_id_valid('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=')
         );
     }
+
+    /**
+     * Test generateSecretApi.
+     */
+    public function testGenerateSecretApi()
+    {
+        $this->assertEquals(12, strlen(generate_api_secret('foo', 'bar')));
+    }
+
+    /**
+     * Test generateSecretApi with invalid parameters.
+     */
+    public function testGenerateSecretApiInvalid()
+    {
+        $this->assertFalse(generate_api_secret('', ''));
+        $this->assertFalse(generate_api_secret(false, false));
+    }
 }
index 983bcd085125923d58806d59f4f87af302b05122..a015770ef1477004af51fcdae44a3921045a6772 100644 (file)
           <label for="updateCheck">&nbsp;Notify me when a new release is ready</label>
         </td>
       </tr>
+      <tr>
+        <td valign="top"><b>Enable API</b></td>
+        <td>
+          <input type="checkbox" name="apiEnabled" id="apiEnabled"
+                 {if="$api_enabled"}checked{/if}/>
+          <label for="apiEnabled">&nbsp;Allow third party software to use Shaarli such as mobile application.</label>
+        </td>
+      </tr>
+      <tr>
+        <td valign="top"><b>API secret</b></td>
+        <td>
+          <input type="text" name="apiSecret" id="apiSecret" size="50" value="{$api_secret}" />
+        </td>
+      </tr>
 
       <tr>
         <td></td>
index 88eb540e1f18b8fc5d61056e2579e695f14cc15e..eda4c54d1c355d1555585b01b2b18dd96193882f 100644 (file)
             <tr><td valign="top"><b>Update:</b></td><td>
                 <input type="checkbox" name="updateCheck" id="updateCheck" checked="checked"><label for="updateCheck">&nbsp;Notify me when a new release is ready</label></td>
             </tr>
+            <tr>
+                <td valign="top">
+                    <b>API:</b>
+                </td>
+                <td>
+                    <input type="checkbox" name="enableApi" id="enableApi" checked="checked">
+                    <label for="enableApi">
+                        &nbsp;Enable Shaarli's API.
+                        Allow third party software to use Shaarli such as mobile application.
+                    </label>
+                </td>
+            </tr>
             <tr><td colspan="2"><input type="submit" name="Save" value="Save config" class="bigbutton"></td></tr>
         </table>
     </form>