SAP container for PEAR::Auth

September 1st, 2006. Tagged: PEAR, SAP

PEAR::Auth is a package that allows you to abstract the user authentication from the main part of your application and not worry about it. What is good about the package is that it comes with different "containers" that allows you to authenticate users against different storages, I mean you can store users data in a database and use the PEAR::DB or PEAR::MDB2 containers, or you can use flat files, IMAP servers, SOAP and what not. And the package is easily extensible. So I played around with creating an SAP container that allows you to check users against your company's SAP system and for example build a section of your Internet (or Extranet) page that is only accessible for people and partners that exist as users in the SAP system.

In order to connect to an SAP system with PHP you need the SAPRFC PHP extension. Get it here. Then you use the function saprfc_open() (more docs here) to establish a connection. You provide some info about the SAP system as well as your username/password. Once connected, a so-called "SSO ticket" is generated for you. This is just a long string, like a session ID. For consecutive connections you can use this SSO ticket instead of providing username/password every time. BTW, SSO stands for Single Sign-On.

Now, with my little SAP container you can benefit from the PEAR and PEAR::Auth infrastructure to do the logins. The way to do an authentication is simple (example stolen in parts from this PEAR manual entry). You pass the connection options (such as hostname). Then, once the user is authenticated, the container retrieves the SSO session ID and sticks into the Auth session data, so that it's reusable for consecutive connections within the same session. If you need to do more with the SAP system, apart from authenticating users, you can get back the updated connection options and just pass them to saprfc_open(). Here's an example:

<?php
// get Auth lib
require_once "Auth.php";
 
// SAP connection options
$options = array (
    'ASHOST'    => 'hostname'
);
 // create Auth object using the SAP container
$a = new Auth("SAP", $options);
 

$a->start();
 
// check
if ($a->checkAuth()) {

 
    // authorised! You can do the protected stuff here
 
    // For example open a connection to the SAP system
    // using the stored authentication data
    $rfc = saprfc_open($a->getAuthData('sap'));
 
    // show sapinfo if you will
    echo '<pre>';
    print_r(saprfc_attributes($rfc));
    echo '</pre>';

}
?>

And here's the actual Auth_Contaner_SAP class, should be placed in a file called SAP.php in your_pear_dir/Auth/Container/

<?php
require_once 'PEAR.php';
require_once 'Auth/Container.php';
/**
 * Performs authentication against an SAP system
 * using the SAPRFC PHP extension.
 *
 * When the option GETSSO2 is TRUE (default)
 * the Single Sign-On (SSO) ticket is retrieved
 * and stored as an Auth attribute called 'sap'
 * in order to be reused for consecutive connections.
 *
 * @author Stoyan Stefanov <ssttoo@gmail.com>
 * @package Auth
 * @see http://saprfc.sourceforge.net/
 */
class Auth_Container_SAP extends Auth_Container {
    /**
     * @var array Default options
     */
    var $options = array(
        'CLIENT'    => '000',
        'LANG'      => 'EN',
        'GETSSO2'   => true,
    );
 
    /**
     * Class constructor. Checks that required options
     * are present and that the SAPRFC extension is loaded
     *
     * Options that can be passed and their defaults:
     * <pre>
     * array(
     *   'ASHOST' => "",
     *   'SYSNR'    => "",
     *   'CLIENT' => "000",
     *   'GWHOST' =>"",
     *   'GWSERV' =>"",
     *   'MSHOST' =>"",
     *   'R3NAME' =>"",
     *   'GROUP'    =>"",
     *   'LANG'     =>"EN",
     *   'TRACE'    =>"",
     *   'GETSSO2'=> true
     * )
     * </pre>
     *
     * @var array array of options.
     */
    function Auth_Container_SAP($options)
    {
        $saprfc_loaded = PEAR::loadExtension('saprfc');
        if (!$saprfc_loaded) {
            return PEAR::raiseError('Cannot use SAP authentication, '
                    .'SAPRFC extension not loaded!');
        }
        if (empty($options['R3NAME']) && empty($options['ASHOST'])) {
            return PEAR::raiseError('R3NAME or ASHOST required for authentication');
        }
        $this->options = array_merge($this->options, $options);
    }

    /**
     * Performs username and password check
     *
     * @var string Username
     * @var string Password
     * @return boolean TRUE on success (valid user), FALSE otherwise
     */        
    function fetchData($username, $password)
    {
        $connection_options = $this->options;
        $connection_options['USER'] = $username;
        $connection_options['PASSWD'] = $password;
        $rfc = saprfc_open($connection_options);
        if (!$rfc) {
            $message = "Couldn't connect to the SAP system.";
            $error = $this->getError();
            if ($error['message']) {
                $message .= ': ' . $error['message'];
            }
            PEAR::raiseError($message, null, null, null, @$erorr['all']);
            return false;
        } else {
            if (!empty($this->options['GETSSO2'])) {
                if ($ticket = @saprfc_get_ticket($rfc)) {
                    $this->options['MYSAPSSO2'] = $ticket;
                    unset($this->options['GETSSO2']);
                    $this->_auth_obj->setAuthData('sap', $this->options);
                } else {
                    PEAR::raiseError("SSO ticket retrieval failed");
                }
            }
            @saprfc_close($rfc);
            return true;
        }

     }
    /**
     * Retrieves the last error from the SAP connection
     * and returns it as an array.
     *
     * @return array Array of error information
     */
    function getError()
    {

        $error = array();
        $sap_error = saprfc_error();
        if (empty($err)) {
            return $error;
        }
        $err = explode("\n", $sap_error);
        foreach ($err AS $line) {
            $item = split(':', $line);
            $error[strtolower(trim($item[0]))] = trim($item[1]);
        }
        $error['all'] = $sap_error;
        return $error;
    }
}
?>

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov