Welcome to the Question2Answer Q&A. There's also a demo if you just want to try it out.
+3 votes
36.4k views
in Q2A Core by
Thanks. Appreciate the pointers.

I now have much of the SSO functionality working with my SMF forum. Here is what my functions in qa-external-users.php look like.

    function qa_get_mysql_user_column_type()
    {
        return 'MEDIUMINT UNSIGNED';
    }


    function qa_get_login_links($relative_url_prefix, $redirect_back_to_url)
    {
        return array(
            'login' => 'https://kulsara.com/forum/index.php?action=login',
            'register' => 'https://kulsara.com/forum/index.php?action=register',
            'logout' => 'https://kulsara.com/forum/index.php?action=logout',
        );

    }


    function qa_get_logged_in_user($qa_db_connection)
    {   
        session_start();

        $cookiename = 'SMFCookie10';

        if (isset($_COOKIE[$cookiename]) && preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}

$~', $_COOKIE[$cookiename]) === 1)
            $var = @unserialize($_COOKIE[$cookiename]);
        else
            $var = @unserialize(stripslashes($_COOKIE[$cookiename]));

        $member_id = $var[0];

        if (@$_COOKIE[$cookiename]) {
            $result=mysql_fetch_assoc(

                mysql_query(
                    "SELECT ID_MEMBER, memberName, emailAddress, ID_GROUP FROM smf_members WHERE ID_MEMBER='$member_id'"
                )

            );
           
            if (is_array($result))
                return array(
                    'userid' => $result['ID_MEMBER'],
                    'publicusername' => $result['memberName'],
                    'email' => $result['emailAddress'],
                    'level' => $result['ID_GROUP'] ? QA_USER_LEVEL_ADMIN : QA_USER_LEVEL_BASIC
                );
        }
   
        return null;
   
    }


    function qa_get_user_email($qa_db_connection, $userid)
    {

        $result=mysql_fetch_assoc(

            mysql_query(
                "SELECT emailAddress FROM smf_members WHERE ID_MEMBER='$userid'"
            )


        );
       
        if (is_array($result))
            return $result['emailAddress'];
       
        return null;

    }


    function qa_get_userids_from_public($qa_db_connection, $publicusernames)
    {       
        $escapedusernames=array();
        foreach ($publicusernames as $publicusername)
            $escapedusernames[]=$publicusername;

       
        $results=mysql_query(
            'SELECT memberName, ID_MEMBER FROM smf_members WHERE memberName IN ('.implode(',', $escapedusernames).')',
            $qa_db_connection
        );

        $publictouserid=array();
       
        while ($result=mysql_fetch_assoc($results))
            $publictouserid[$result['memberName']]=$result['ID_MEMBER'];
       
        return $publictouserid;


    }


    function qa_get_public_from_userids($qa_db_connection, $userids)
    {

        $escapeduserids=array();
        foreach ($userids as $userid)
            $escapeduserids[]="'".mysql_real_escape_string($userid, $qa_db_connection)."'";
       
        $results=mysql_query(
            'SELECT memberName, ID_MEMBER FROM smf_members WHERE ID_MEMBER IN ('.implode(',', $escapeduserids).')',
            $qa_db_connection
        );


        $useridtopublic=array();
       
        while ($result=mysql_fetch_assoc($results))
            $useridtopublic[$result['ID_MEMBER']]=$result['memberName'];
       
        return $useridtopublic;

    }



What works using the above code is the following: Basic SSO works. So if I am logged into my SMF forum, I am showed as logged into QA with the correct username at the top. The "Ask a Question" and "Users" tabs also seem to work fine, e.g. in Users I can see the username of the user that posted a question with some points next to the username.

There are a couple of issues that I am still having trouble with:

(1) When I click the Questions tab, I get a warning stating:

Warning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource in qa/qa-external/qa-external-users.php on line 490

Line 490 is the mysql_fetch_assoc() in the function qa_get_public_from_userids

(2) I am still trying to resolve the logout link in the qa_get_login_links function, since I need to include the session id while logging out, which is how SMF does the logout.

Appreciate any pointers here. Thanks again.
related to an answer for: Still working on the $_COOKIE

2 Answers

0 votes
by
 
Best answer
Hi there,

Congratulations on the progress. Before I answer your question, I can't stress enough the importance of escaping the parameters you're passing through to MySQL with mysql_real_escape_string() - there are a few functions above where you're not doing this, or you removed the escaping from the sample code. This exposes your database to SQL injection attacks.

Now, to answer your questions:

1) I'm guessing there's an error occurring in the query you're sending to the database. Try doing the following before line 490:

if (!$results) echo mysql_error($qa_db_connection);

That should output the error to the browser window and we'll be wiser.

2) A quick solution is to just return null for the logout link, and it won't be shown. Otherwise, you will need to get the session ID out of SMF's state - this will probably be somewhere in $_SESSION or $_COOKIE, but you'd need to consult SMF's documentation or source code.
+2 votes
by

For SMF 2.0, here is a qa-external-users.php that works for me. Note I had to trim out the comments and remove (unchanged) qa_get_logged_in_user_html, qa_get_users_html, qa_avatar_html_from_userid, and qa_user_report_action to make it postable here.


<?php
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
    header('Location: ../');
    exit;
}

function qa_get_mysql_user_column_type()
{
    return 'MEDIUMINT UNSIGNED';
}

function qa_get_login_links($relative_url_prefix, $redirect_back_to_url)
{
    //    Until you edit this function, don't show login, register or logout links

    return array(
        'login' => 'https://site.com/forums/index.php?action=login',
        'register' => 'https://site.com/forums/index.php?action=register',
        'logout' => 'https://site.com/forums/index.php?action=logout'
    );
}

function qa_get_logged_in_user()
{
global $smcFunc;

    $cookiename = 'SMFCookie10';

    if (isset($_COOKIE[$cookiename]) && preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~', $_COOKIE[$cookiename]) === 1) {
        $var = @unserialize(($_COOKIE[$cookiename]));
    //$var = @unserialize(rawurldecode($_COOKIE[$cookiename]));
    } else {
        $var = @unserialize(stripslashes($_COOKIE[$cookiename]));
    }

    $member_id = $var[0];
    
    if (@$_COOKIE[$cookiename]) {
        $result = $smcFunc['db_query'] ('', "SELECT m.id_member, m.member_name, m.email_address, m.id_group FROM {db_prefix}members AS m WHERE m.id_member={int:id_member}", array (
            'id_member' => $member_id
        ));

        if (!$result) {
            die("$query failed : " . $smcFunc['db_error'] ());
        }

        $res = $smcFunc['db_fetch_assoc'] ($result);

        if (is_array($res)) {
            return array(
                'userid' => $res['id_member'],
                'publicusername' => $res['member_name'],
                'email' => $res['email_address'],
                'level' => $res['id_group'] ? QA_USER_LEVEL_ADMIN : QA_USER_LEVEL_BASIC
            );
        }
        
        $smcFunc['db_free_result'] ($result);
    }

    return null;
}

function qa_get_user_email($userid)
{

global $smcFunc;

    $result = $smcFunc['db_query'] ('', "SELECT m.email_address FROM {db_prefix}members AS m WHERE m.id_member={int:id_member}", array (
        'id_member' => $member_id
    ));

    if (!$result) {
        die("$query failed : " . $smcFunc['db_error'] ());
    }

    $res = $smcFunc['db_fetch_assoc'] ($result);

    if (is_array($res)) {
        return $res['email_address'];
    }
    
    $smcFunc['db_free_result'] ($result);

    return null;

}

function qa_get_userids_from_public($publicusernames)
{
global $smcFunc;

    $publictouserid = array();

    if (count($publicusernames)) {
        $escapedusernames = array();
        foreach ($publicusernames as $publicusername)
            $escapedusernames[] = "'" . qa_db_escape_string($publicusername) . "'";

        $result = $smcFunc['db_query'] ('', "SELECT m.member_name, m.id_member FROM {db_prefix}members AS m WHERE m.member_name IN (" . implode(',', $escapedusernames) . ")", "");

        if (!$result) {
            die("$query failed : " . $smcFunc['db_error'] ());
        }
        
        while ($res = $smcFunc['db_fetch_assoc'] ($result)) {
            $publictouserid[$result['member_name']] = $result['id_member'];
        }

        $smcFunc['db_free_result'] ($result);
    }

    return $publictouserid;

}

function qa_get_public_from_userids($userids)
{
global $smcFunc;

    $useridtopublic = array();

    if (count($publicusernames)) {
        $escapedusernames = array();
        foreach ($publicusernames as $publicusername)
            $escapedusernames[] = "'" . qa_db_escape_string($publicusername) . "'";

        $result = $smcFunc['db_query'] ('', "SELECT m.member_name, m.id_member FROM {db_prefix}members AS m WHERE m.id_member IN " . implode(',', $escapedusernames) . ")'", "");

        if (!$result) {
            die("$query failed : " . $smcFunc['db_error'] ());
        }
        
        while ($res = $smcFunc['db_fetch_assoc'] ($result)) {
            $useridtopublic[$result['id_member']] = $result['member_name'];
        }

        $smcFunc['db_free_result'] ($result);
    }

    return $useridtopublic;
}


You have to add the following to the top of your qa-config.php, to load the SMF environment (E.G. just below the database DEFINEs):

// Load the SMF environment

require_once ('/var/www/html/forums/SSI.php')

...