PHP api like Aggregate

I coding on php based api to collect data from ODK Collect, its working fine but i implement PHP digest authentication for security when download forms and send finalized forms. But the problem is dialog pop up on ODK collect each time user what to download and send finalized forms rather that pick from shared preference.
I am using Codeigniter Framework. Need help from you guys

PHP Digest Authentication codes:

  1. Xform Controller

    public function submission()
    {
    // Form Received in openrosa server
    $http_response_code = 201;
    // Get the digest from the http header
    if (isset($_SERVER ['PHP_AUTH_DIGEST']))
    $digest = $_SERVER ['PHP_AUTH_DIGEST'];
    // server realm and unique id
    $realm = $this->config->item("realm");
    $nonce = md5(uniqid());
    // Check if there was no digest, show login
    if (empty ($digest)) {
    // populate login form if no digest authenticate
    $this->form_auth->require_login_prompt($realm, $nonce);
    log_message('debug', 'exiting, digest was not found');
    exit ();
    }
    // http_digest_parse
    $digest_parts = $this->form_auth->http_digest_parse($digest);
    // username obtained from http digest
    $username = $digest_parts ['username'];
    // get user details from database
    $user = $this->User_model->find_by_username($username);
    $password = $user->digest_password; // digest password
    $db_username = $user->username; // username
    $uploaded_filename = NULL;
    // show status header if user not available in database
    if (empty ($db_username)) {
    // populate login form if no digest authenticate
    $this->form_auth->require_login_prompt($realm, $nonce);
    log_message('debug', 'username is not available');
    exit ();
    }
    // Based on all the info we gathered we can figure out what the response should be
    $A1 = $password; // digest password
    $A2 = md5("{$_SERVER['REQUEST_METHOD']}:{$digest_parts['uri']}");
    $calculated_response = md5("{$A1}:{$digest_parts['nonce']}:{$digest_parts['nc']}:{$digest_parts['cnonce']}:{$digest_parts['qop']}:{$A2}");
    // If digest fails, show login
    if ($digest_parts ['response'] != $calculated_response) {
    // populate login form if no digest authenticate
    $this->form_auth->require_login_prompt($realm, $nonce);
    log_message('debug', 'Digest does not match');
    exit ();
    }
    // IF passes authentication
    if ($_SERVER ['REQUEST_METHOD'] === "HEAD") {
    $http_response_code = 204;
    } elseif ($_SERVER ['REQUEST_METHOD'] === "POST") {
    foreach ($_FILES as $file) {
    // File details
    $file_name = $file ['name'];
    // check file extension
    $value = explode('.', $file_name);
    $file_extension = end($value);
    $inserted_form_id = NULL;
    if ($file_extension === 'xml') {
    // path to store xml
    $uploaded_filename = $file_name;
    $path = $this->config->item("form_data_upload_dir") . $file_name;
    // insert form details in database
    $data = array(
    'file_name' => $file_name,
    'user_id' => $user->id
    );
    $inserted_form_id = $this->Submission_model->create($data);
    } elseif ($file_extension == 'jpg' or $file_extension == 'jpeg' or $file_extension == 'png') {
    // path to store images
    $path = $this->config->item("images_data_upload_dir") . $file_name;
    //TODO Resize image here
    } elseif ($file_extension == '3gpp' or $file_extension == 'amr') {
    // path to store audio
    $path = $this->config->item("audio_data_upload_dir") . $file_name;
    } elseif ($file_extension == '3gp' or $file_extension == 'mp4') {
    // path to store video
    $path = $this->config->item("video_data_upload_dir") . $file_name;
    }
    // upload file to the server
    move_uploaded_file($file ['tmp_name'], $path);
    }
    // call function to insert xform data in a database
    if (!$this->_insert($uploaded_filename)) {
    if ($this->Submission_model->delete_submission($inserted_form_id))
    @unlink($path);
    }
    }
    // return response
    $this->get_response($http_response_code);
    }

  2. Form_Auth library

class Form_auth
{

private $CI;

public function __construct()
{
	$this->CI =& get_instance();
	log_message('debug', 'Form_auth library initialized');
}


// This function forces a login prompt
function require_login_prompt($realm, $nonce)
{
	$qop = "auth";
	//http_response_code(401);
	header("HTTP/1.1 401 Unauthorized");
	header('Content-Type: text/xml; charset=utf-8');
	header('WWW-Authenticate: Digest realm="' . $realm . '",qop="auth",nonce="' . $nonce . '",opaque="' . $nonce . '"');
	header('"HTTP_X_OPENROSA_VERSION": "1.0"');
	header('X-OpenRosa-Version:1.0');
}

// This function extracts the separate values from the digest string
function http_digest_parse($digest)
{
	// protect against missing data
	$needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
	$data = array();

	preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER);

	foreach ($matches as $m) {
		$data[$m[1]] = $m[2] ? $m[2] : $m[3];
		unset($needed_parts[$m[1]]);
	}

	return $needed_parts ? FALSE : $data;
}

}

Hi,

Search this list for other PHP-related posts and you might get some
insights. Based on your description, you probably haven't implemented
digest auth according to the spec.

Sniff the traffic on the wire when you use ODK Collect to Aggregate
and compare that with your server.

Yaw

ยทยทยท -- Need ODK consultants? Nafundi provides form design, server setup, in-field training, and software development for ODK. Go to https://nafundi.com to get started.

On Mon, Apr 11, 2016 at 10:47 AM, renfridfrancis@gmail.com wrote:

I coding on php based api to collect data from ODK Collect, its working fine but i implement PHP digest authentication for security when download forms and send finalized forms. But the problem is dialog pop up on ODK collect each time user what to download and send finalized forms rather that pick from shared preference.
I am using Codeigniter Framework. Need help from you guys

PHP Digest Authentication codes:

  1. Xform Controller

    public function submission()
    {
    // Form Received in openrosa server
    $http_response_code = 201;
    // Get the digest from the http header
    if (isset($_SERVER ['PHP_AUTH_DIGEST']))
    $digest = $_SERVER ['PHP_AUTH_DIGEST'];
    // server realm and unique id
    $realm = $this->config->item("realm");
    $nonce = md5(uniqid());
    // Check if there was no digest, show login
    if (empty ($digest)) {
    // populate login form if no digest authenticate
    $this->form_auth->require_login_prompt($realm, $nonce);
    log_message('debug', 'exiting, digest was not found');
    exit ();
    }
    // http_digest_parse
    $digest_parts = $this->form_auth->http_digest_parse($digest);
    // username obtained from http digest
    $username = $digest_parts ['username'];
    // get user details from database
    $user = $this->User_model->find_by_username($username);
    $password = $user->digest_password; // digest password
    $db_username = $user->username; // username
    $uploaded_filename = NULL;
    // show status header if user not available in database
    if (empty ($db_username)) {
    // populate login form if no digest authenticate
    $this->form_auth->require_login_prompt($realm, $nonce);
    log_message('debug', 'username is not available');
    exit ();
    }
    // Based on all the info we gathered we can figure out what the response should be
    $A1 = $password; // digest password
    $A2 = md5("{$_SERVER['REQUEST_METHOD']}:{$digest_parts['uri']}");
    $calculated_response = md5("{$A1}:{$digest_parts['nonce']}:{$digest_parts['nc']}:{$digest_parts['cnonce']}:{$digest_parts['qop']}:{$A2}");
    // If digest fails, show login
    if ($digest_parts ['response'] != $calculated_response) {
    // populate login form if no digest authenticate
    $this->form_auth->require_login_prompt($realm, $nonce);
    log_message('debug', 'Digest does not match');
    exit ();
    }
    // IF passes authentication
    if ($_SERVER ['REQUEST_METHOD'] === "HEAD") {
    $http_response_code = 204;
    } elseif ($_SERVER ['REQUEST_METHOD'] === "POST") {
    foreach ($_FILES as $file) {
    // File details
    $file_name = $file ['name'];
    // check file extension
    $value = explode('.', $file_name);
    $file_extension = end($value);
    $inserted_form_id = NULL;
    if ($file_extension === 'xml') {
    // path to store xml
    $uploaded_filename = $file_name;
    $path = $this->config->item("form_data_upload_dir") . $file_name;
    // insert form details in database
    $data = array(
    'file_name' => $file_name,
    'user_id' => $user->id
    );
    $inserted_form_id = $this->Submission_model->create($data);
    } elseif ($file_extension == 'jpg' or $file_extension == 'jpeg' or $file_extension == 'png') {
    // path to store images
    $path = $this->config->item("images_data_upload_dir") . $file_name;
    //TODO Resize image here
    } elseif ($file_extension == '3gpp' or $file_extension == 'amr') {
    // path to store audio
    $path = $this->config->item("audio_data_upload_dir") . $file_name;
    } elseif ($file_extension == '3gp' or $file_extension == 'mp4') {
    // path to store video
    $path = $this->config->item("video_data_upload_dir") . $file_name;
    }
    // upload file to the server
    move_uploaded_file($file ['tmp_name'], $path);
    }
    // call function to insert xform data in a database
    if (!$this->_insert($uploaded_filename)) {
    if ($this->Submission_model->delete_submission($inserted_form_id))
    @unlink($path);
    }
    }
    // return response
    $this->get_response($http_response_code);
    }

  2. Form_Auth library

class Form_auth
{

    private $CI;

    public function __construct()
    {
            $this->CI =& get_instance();
            log_message('debug', 'Form_auth library initialized');
    }


    // This function forces a login prompt
    function require_login_prompt($realm, $nonce)
    {
            $qop = "auth";
            //http_response_code(401);
            header("HTTP/1.1 401 Unauthorized");
            header('Content-Type: text/xml; charset=utf-8');
            header('WWW-Authenticate: Digest realm="' . $realm . '",qop="auth",nonce="' . $nonce . '",opaque="' . $nonce . '"');
            header('"HTTP_X_OPENROSA_VERSION": "1.0"');
            header('X-OpenRosa-Version:1.0');
    }

    // This function extracts the separate values from the digest string
    function http_digest_parse($digest)
    {
            // protect against missing data
            $needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
            $data = array();

            preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER);

            foreach ($matches as $m) {
                    $data[$m[1]] = $m[2] ? $m[2] : $m[3];
                    unset($needed_parts[$m[1]]);
            }

            return $needed_parts ? FALSE : $data;
    }

}

--
You received this message because you are subscribed to the Google Groups "ODK Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to opendatakit-developers+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.