]> git.defcon.no Git - hermes/blobdiff - api/lib/auth_base.php
Forgot one instance of 'permitedcalls' ...
[hermes] / api / lib / auth_base.php
index ae8a5ebf9989bdea71d30417757d3a03a1252f6c..0bf65f75eea7900dbc4dc0c5209f9e50545e4c85 100644 (file)
@@ -1,8 +1,63 @@
 <?php
+/*
+#  Copyright (c) 2012, Gjøvik University College
+#  All rights reserved.
+
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#      * Redistributions of source code must retain the above copyright
+#        notice, this list of conditions and the following disclaimer.
+#      * Redistributions in binary form must reproduce the above copyright
+#        notice, this list of conditions and the following disclaimer in the
+#        documentation and/or other materials provided with the distribution.
+#      * Neither the name of the Gjøvik University College nor the
+#        names of its contributors may be used to endorse or promote products
+#        derived from this software without specific prior written permission.
+#  
+#  THIS SOFTWARE IS PROVIDED BY Gjøvik University College ''AS IS'' AND ANY
+#  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+#  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+#  DISCLAIMED. IN NO EVENT SHALL Gjøvik University College BE LIABLE FOR ANY
+#  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+#  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+#  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
 require_once('config.php');
 
 $config = get_config();
 
+function authlevel_value( $level )
+{
+       switch ( $level )
+       {
+               case 'limited_read':
+                       return 1;
+               case 'full_read':
+                       return 2;
+               case 'read_write':
+                       return 3;
+               default:
+                       return 0;
+       }
+}
+function authlevel_name( $level )
+{
+       switch ( $level )
+       {
+               case 1:
+                       return 'limited_read';
+               case 2:
+                       return 'full_read';
+               case 3:
+                       return 'read_write';
+               default:
+                       return 'no_access';
+       }
+}
+
 /*******************************
 * Load authentication plugin ..
 *******************************/
@@ -15,15 +70,7 @@ else
 {  print json_encode( array( 'response' => 'error', 'cause' => 'config-error' ) ); exit; }
 /*******************************/
 
-function check_authkey ( $key )
-{
-       global $config;
-       if ( $key == "6327c08b70f9" ) return true;
-       return false;
-
-}
-
-function new_key(  )
+function new_key( $hex = false )
 {
        // Basically this is at the moment a slightly modified
        // version of generate_password() from user_functiions.php
@@ -33,10 +80,15 @@ function new_key(  )
        $string = "";
         while ( strlen( $string ) < $length )
         {
-               $string .= crypt( substr(sha1(rand()), 0, $length) );
-               $string = preg_replace( '/\W/', '', $string);
+               if ( $hex )
+                       $string .= substr(md5(rand().rand()), 0, $length+1);
+               else
+               {
+                       $string .= crypt( substr(sha1(rand()), 0, $length+1) );
+                       $string = preg_replace( '/\W/', '', $string);
+               }
         }
-       return substr( $string, 0, $length );
+       return substr( $string, 1, $length );
 }
 
 function simple_authfail()
@@ -47,17 +99,346 @@ function simple_authfail()
 
 function token_auth( )
 {
-       global $_GET;
+       global $_POST;
+
+       // TODO: Part of ping/pong requirement.
+       // Run a function to clear all authkeys older than 5 minutes.
+       expire_authkeys();
 
-       if ( array_key_exists('auth_key', $_GET ) )
-       { if ( ! check_authkey($_GET['auth_key'] ) ) simple_authfail(); }
+       if ( array_key_exists('session', $_POST ) 
+            && array_key_exists('auth_key', $_POST ) )
+       { 
+               if ( ! check_session($_POST['session'] ) ) simple_authfail();
+               if ( ! check_authkey($_POST['auth_key'] ) ) simple_authfail(); 
+       }
        else simple_authfail();
 }
 
-function can_write ( )
+function get_cookie_path ()
+{
+       $name = $_SERVER["SCRIPT_NAME"];
+       $file = basename($name);
+       $path = preg_replace("/".$file."/", "", $name);
+       return $path;
+
+}
+
+function check_authkey ( $key )
+{
+       // TODO: Make real, actual checks...
+       if ( $key ) return true;
+       return false;
+}
+
+function expire_authkeys()
+{
+       global $config;
+
+       // Force deletion of sessions that have expired keys.
+       $query = sprintf("SELECT session, sessid FROM %s WHERE `last` < DATE_SUB( NOW(), INTERVAL %d MINUTE)",
+               $config['sessionkeys_table'],
+               $config['sessionkey_lifetime']);
+       $result = sql_dbquery( $config['hermes_db'], $query );
+       while ( $row = @mysql_fetch_row( $result ) )
+       {
+               remove_session( $row[0], $row[1] );
+       }
+
+       $query = sprintf("DELETE FROM %s WHERE `last` < DATE_SUB( NOW(), INTERVAL %d MINUTE)",
+               $config['sessionkeys_table'],
+               $config['sessionkey_lifetime']);
+
+       sql_dbexec( $config['hermes_db'], $query );
+}
+
+function update_authkey ( $session, $authid )
+{
+       global $config;
+
+       $key = substr(new_key(), 0, 8);
+
+       expire_authkeys();
+
+       // TODO: Refresh cookie
+
+       $remote = $_SERVER['REMOTE_ADDR'];
+       $query = sprintf("INSERT INTO %s ( `sessid`, `session`, `authid`, `client`, `key`, `last` )
+                               VALUES ( '%s', '%s', '%s', '%s', '%s', NOW() )
+                               ON DUPLICATE KEY UPDATE `key` = '%s', `last` = NOW()",
+               $config['sessionkeys_table'],
+               session_id(),
+               session_name(),
+               sql_clean($authid),
+               sql_clean($remote),
+               sql_clean($key),
+               sql_clean($key));
+       if ( ! sql_dbexec( $config['hermes_db'], $query ) )
+       {
+               mysql_error();
+       }
+       $_SESSION['kkey'] = $key;
+       $_SESSION['when'] = time();
+       return $key;
+}
+
+function check_session ( $name )
+{
+       session_name( $name );
+       session_start();
+       if ( ! $_SESSION['authid'] )
+       {
+               return clear_credentials($name);
+       }
+       if ( ! $_COOKIE['client_key'] )
+       {
+               return clear_credentials($name);
+       }
+
+       $authid = $_SESSION['authid'];
+       $type   = $_SESSION['type'];
+       $client_key = $_COOKIE['client_key'];
+
+       $level = get_authorization( $type, $authid );
+       if ( $level == false )
+       {
+               return clear_credentials($name);
+       }
+
+       $session_key = md5( $name . $authid );
+       if ( $client_key != $session_key )
+       {
+               return clear_credentials($name);
+       }
+
+       // If we got this far, things are looking good.
+       return true;
+}
+
+function set_credentials( $authid, $type )
+{
+       $name = new_key(true);
+       session_name( $name );
+       session_start();
+       $_SESSION['authid'] = $authid;
+       $_SESSION['type']   = $type;
+
+       $client_key  = md5( $name . $authid );
+       setcookie('client_key', $client_key, time()+180*60, get_cookie_path() );
+
+       return $name;
+}
+
+function clear_credentials($name)
+{
+       global $config;
+
+       setcookie('client_key', '', 0, get_cookie_path() );
+
+       remove_session($name);
+       $_SESSION = array();
+
+       $query = sprintf("DELETE FROM %s WHERE `session` = '%s'",
+               $config['sessionkeys_table'],
+               sql_clean($name));
+       sql_dbexec( $config['hermes_db'], $query );
+
+       return false;
+}
+
+function remove_session ($name, $id = null )
+{
+       if ( $id == null )
+       {
+               session_destroy();
+               setcookie($name, '', 0, "/");
+               return;
+       }
+       $current_session = session_name( );
+       $current_sessid = session_id( );
+       session_commit();
+
+       session_id( $id );
+       session_start();
+       setcookie( $name, '', 0, "/");
+       $_SESSION=array();
+       session_destroy();
+
+       if ( $current_session && $current_session != $name )
+       {
+               session_id($current_sessid);
+               session_start();
+       }
+}
+function add_apikey ( $host, $level )
+{
+       global $config;
+       if ( !is_numeric($level) ) return false;
+
+       $key = new_key();
+
+       // Try to add the new key to authorizations first. If this
+       // fails, there will be the least amount of data to clean up ...
+       if ( ! update_authorization( "key", $key, $level ) ) return false;
+
+       $query = sprintf("INSERT INTO %s ( host, apikey ) VALUES ( '%s', '%s' )",
+               $config['apikeys_table'],
+               sql_clean($host),
+               sql_clean($key));
+
+       if ( ! sql_dbexec( $config['hermes_db'], $query ) ) return false;
+       return $key;
+}
+
+function remove_apikey( $key )
+{
+       global $config;
+       if ( ! verify_apikey( $key, true ) ) return false;
+       if ( ! remove_authorization( $key ) ) return false;
+
+       $query = sprintf("DELETE FROM %s WHERE apikey = '%s'",
+               $config['apikeys_table'],
+               sql_clean($key) );
+       if ( ! sql_dbexec( $config['hermes_db'], $query ) ) return false;
+
+       return true;
+}
+
+function verify_apikey( $key, $skip_hostcheck = false )
+{
+       global $config;
+
+       $query = sprintf("SELECT host FROM %s WHERE apikey = '%s'",
+               $config['apikeys_table'],
+               sql_clean($key) );
+       $row = sql_dbquery_single( $config['hermes_db'], $query );
+       if  (!$row) return false;
+       $host = $row['host'];
+
+       if ( $host && ( $skip_hostcheck ) )
+               return true;
+
+       if ( $host == $_SERVER['REMOTE_ADDR'] ) return true;
+       return false;
+
+}
+
+function list_apikeys ()
+{
+       global $config;
+       $query = sprintf("SELECT k.apikey AS apikey, k.host AS host, 
+                               a.access_level AS access_level 
+                               FROM %s k INNER JOIN %s a ON k.apikey = a.authid
+                               WHERE a.type = 'key'",
+                       $config['apikeys_table'], 
+                       $config['authorizations_table']);
+       $list = array();
+       $result = sql_dbquery( $config['hermes_db'], $query);
+       if ( ! $result ) return $list; 
+       while ( $row = @mysql_fetch_assoc( $result ) )
+       {
+               array_push( $list, array(
+                       'api_key' => $row['apikey'],
+                       'host' => $row['host'],
+                       'level' => authlevel_name( $row['access_level'] )
+               ));
+       }
+       return $list;
+
+}
+
+function list_authusers ()
+{
+       global $config;
+       $query = sprintf("SELECT authid, access_level
+                               FROM %s
+                               WHERE type = 'user'",
+                       $config['authorizations_table']);
+       $list = array();
+       $result = sql_dbquery( $config['hermes_db'], $query);
+       if ( ! $result ) return $list; 
+       while ( $row = @mysql_fetch_assoc( $result ) )
+       {
+               $username = $row['authid'];
+               $user_data = authuser_getinfo( $username );
+
+               // TODO: Remove invalid users here?
+               if ( ! $user_data ) continue;
+
+               array_push( $list, array(
+                       'user' => $username,
+                       'name' => $user_data['name'],
+                       'email' => $user_data['email'],
+                       'level' => authlevel_name( $row['access_level'] )
+               ));
+       }
+       return $list;
+
+}
+
+
+
+function update_authorization( $type, $authid, $level )
 {
-       // Stub, to be called on any API nodes taht write data in the DB.
+       global $config;
+       if ( !is_numeric($level) ) return false;
+       if ( ($type != "key") && ($type != "user") ) return false;
+
+       $query = sprintf("INSERT INTO %s ( authid, type, access_level ) VALUES ( '%s', '%s', %d )
+                       ON DUPLICATE KEY UPDATE access_level=%d",
+               $config['authorizations_table'],
+               sql_clean($authid),
+               $type,
+               $level, $level);
+       if ( ! sql_dbexec( $config['hermes_db'], $query ) ) return false;
        return true;
 }
 
+function remove_authorization( $authid )
+{
+       global $config;
+       $query = sprintf("DELETE FROM %s WHERE authid = '%s'",
+               $config['authorizations_table'],
+               sql_clean($authid) );
+       //print $query . "\n\n";
+       if ( ! sql_dbexec( $config['hermes_db'], $query ) ) return false;
+       return true;
+}
+
+
+function get_authorization( $type, $authid )
+{
+       global $config;
+
+       // If API-key is used, but key fails verification, write is impossible.
+       if ( ( $type == "key" ) && ( ! verify_apikey( $authid, true ) ) )
+               return false;
+
+       // If User-login is used, but backend is unable to provide info, fail.
+       if ( ( $type == "user" ) && ( ! authuser_getinfo( $authid ) ) )
+               return false;
+
+       // The only types of access control supported are "user" or "key".
+       if ( ($type != "user" ) && ($type != "key") )
+               return false;
+
+       $query = sprintf("SELECT access_level FROM %s  WHERE authid = '%s'",
+               $config['authorizations_table'],
+               sql_clean($authid) );
+       $row = sql_dbquery_single( $config['hermes_db'], $query );
+       if  (!$row) return false;
+       $level = $row['access_level'];
+       return $level;
+}
+
+function can_write ( )
+{
+       // Stub, to be called on any API nodes that write data in the DB.
+       $authid = $_SESSION['authid'];
+       $type   = $_SESSION['type'];
+
+       $level = get_authorization( $type, $authid );
+       if ( $level >= authlevel_value('read_write') ) return $level;
+       else return false;
+}
+
 ?>