Implements PlSession::apiAuth. The authentication is based on a HMAC
authorVincent Zanotti <vincent.zanotti@m4x.org>
Mon, 3 Jan 2011 20:08:48 +0000 (21:08 +0100)
committerVincent Zanotti <vincent.zanotti@m4x.org>
Tue, 4 Jan 2011 01:26:34 +0000 (02:26 +0100)
signature, which takes into account the resource, the payload, and the
current timestamp.

It effectively blocks any replay beyond "the same method, within 10
seconds", which is deemed an acceptable risk, as long as API methods are
idempotent.

Signed-off-by: Vincent Zanotti <vincent.zanotti@m4x.org>
classes/xorgsession.php
configs/platal.ini
core

index f46107c..83bf850 100644 (file)
@@ -258,6 +258,54 @@ class XorgSession extends PlSession
         }
     }
 
+    /**
+     * The authentication schema is based on three query parameters:
+     *   ?user=<hruid>&timestamp=<timestamp>&sig=<sig>
+     * where:
+     *   - hruid is the hruid of the querying user
+     *   - timestamp is the current UNIX timestamp, which has to be within a
+     *     given distance of the server-side UNIX timestamp
+     *   - sig is the HMAC of "<method>#<resource>#<payload>#<timestamp>" using
+     *     a known secret of the user as the key.
+     *
+     * At the moment, the shared secret of the user is the sha1 hash of its
+     * password. This is temporary, though, until better support for tokens is
+     * implemented in plat/al.
+     * TODO(vzanotti): Switch to dedicated secrets for authentication.
+     */
+    public function apiAuth($method, $resource, $payload)
+    {
+        // Verify that the timestamp is within acceptable bounds.
+        $timestamp = Env::i('timestamp', 0);
+        if (abs($timestamp - time()) > Platal::globals()->api->timestamp_tolerance) {
+            return null;
+        }
+
+        // Retrieve the user corresponding to the forlife. Note that at the
+        // moment, other aliases are also accepted.
+        $user = User::getSilent(Env::s('user', ''));
+        if (is_null($user) || !$user->isActive()) {
+            return null;
+        }
+
+        // Determine the list of tokens associated with the user. At the moment,
+        // this is just the sha1 of the password.
+        $tokens = array($user->password());
+
+        // For each token, try to validate the signature.
+        $message = implode('#', array($method, $resource, $payload, $timestamp));
+        $signature = Env::s('sig');
+        foreach ($tokens as $token) {
+            $expected_signature = hash_hmac(
+                Platal::globals()->api->hmac_algo, $message, $token);
+            if ($signature == $expected_signature) {
+                return $user;
+            }
+        }
+
+        return null;
+    }
+
     public function tokenAuth($login, $token)
     {
         $res = XDB::query('SELECT  a.uid, a.hruid
index 7263936..7459807 100644 (file)
@@ -156,6 +156,21 @@ register_skin = "register"
 econfiance = ""
 
 
+; The API section contains  the configuration for the web services.
+[Api]
+
+; $globals->api->hmac_algo
+; Algorithm to use for HMAC-based authentication of API requests. Note that this
+; value is shared with clients, and must be changed in all places at once.
+hmac_algo = "sha256"
+
+; $globals->api->timestamp_tolerance
+; Maximum number of seconds of drift allowed between the client-side UNIX clock
+; and the server-side clock. This should be big enough to also allow for network
+; latency, but not too high, to limit replay opportunities.
+timestamp_tolerance = 10
+
+
 ; The banana section contains the configuration of the forums.
 [Banana]
 
@@ -214,6 +229,7 @@ gmaps_hl = "fr"
 ; Default location preference.
 gmaps_gl = "fr"
 
+
 ; The lists section contains parameters used to interact with mailman.
 [Lists]
 
diff --git a/core b/core
index a59a444..504647c 160000 (submodule)
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit a59a4446b3f96e25620e98d4aab14860dd2ee951
+Subproject commit 504647c51aac2a34a4c3f11f33cc4fa6eecdb64f