I have some PHP code that creates a Facebook test account in my application using the graph api. I've tested this code out, and it works fine.
I'm having trouble actually logging into Facebook as the test user using Selenium Webdriver (for PHP).
Here's a simplified version of my code:
class FB_OAuthTest extends PHPUnit_Framework_TestCase {
private $fbUtils = null;
private $fbUser = null;
private $startUrl = 'http://my.start.page';
protected function setUp() {
$this->webdriver = new WebDriver(Config::SELENIUM_SERVER, Config::SELENIUM_PORT);
$this->webdriver->connect(Config::BROWSER);
$this->fbUtils = new FBUtils(Config::FB_APP_ID, Config::FB_APP_SECRET);
// create new FB test user here
$this->fbUser = $this->fbUtils->createNewTestUser();
}
protected function tearDown() {
// delete the test user when we're done
if($this->fbUser != null) {
$this->fbUtils->deleteTestUser($this->fbUser->id);
}
$this->webdriver->close();
}
// the magic happens here
public function testOAuth() {
// "login" as test user by requesting the login url that they give
$this->webdriver->get($this->fbUser->login_url);
// start the app workflow: go to a page with a FB OAuth button
$this->webdriver->get($this->startUrl);
$this->assertTrue(isTextPresent("FB_OAuth_Test_Start", $this->webdriver));
// click the button to begin the FB OAuth process
$oAuthButton = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//button[#control='FBLogin1']");
$oAuthButton->click();
// The Facebook login/OAuth dialog shows up now, and forces me to login
// despite first going to the FB user's login url
// sleep for the hell of it, just to make sure the FB login dialog loads
sleep(5);
// Selenium fails here - not finding the input with id='email', despite it existing on the page
$emailField = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[id='email']");
if ($emailField) {
$emailField->sendKeys(array($this->fbUser->email));
$emailField->submit();
} else {
$this->fail('FB login email field not found');
}
$passwordField = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[id='pass']");
if ($passwordField) {
$passwordField->sendKeys(array($this->fbUser->password));
$passwordField->submit();
} else {
$this->fail('FB login password field not found');
}
$loginButton = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[name='login']");
if ($loginButton) {
$loginButton->click();
} else {
$this->fail('FB login button not found');
}
$grantAppPermission = $this->webdriver->findElementBy(LocatorStrategy::name, "grant_clicked");
$grantAppPermission->click();
$this->assertTrue(isTextPresent("FB_OAuth_Test_Success", $this->webdriver));
}
}
As you can see from the code comments, Selenium can't find the 'email' input element, and the test fails. Any ideas would be greatly appreciated. Thanks!
Update
I've even tried doing something fairly direct, like the code blow, and it still doesn't work.
private function loginToFacebook() {
$this->webdriver->get('https://www.facebook.com/login.php?login_attempt=1');
sleep(1);
$emailField = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[id='email']");
if ($emailField) {
$emailField->sendKeys(array($this->fbUser->email));
$emailField->submit();
} else {
$this->fail('FB login email field not found');
}
$passwordField = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[id='pass']");
if ($passwordField) {
$passwordField->sendKeys(array($this->fbUser->password));
$passwordField->submit();
} else {
$this->fail('FB login password field not found');
}
$loginButton = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[name='login']");
if ($loginButton) {
$loginButton->click();
} else {
$this->fail('FB login button not found');
}
}
Update: Here's the Working Code
private function loginToFacebook() {
$this->webdriver->get('https://www.facebook.com/login.php?login_attempt=1');
$emailField = $this->webdriver->findElementBy(LocatorStrategy::id, 'email');
if ($emailField) {
$emailField->sendKeys(array($this->fbUser->email));
} else {
$this->fail('FB login email field not found');
}
$passwordField = $this->webdriver->findElementBy(LocatorStrategy::id, 'pass');
if ($passwordField) {
$passwordField->sendKeys(array($this->fbUser->password));
} else {
$this->fail('FB login password field not found');
}
$loginButton = $this->webdriver->findElementBy(LocatorStrategy::xpath, "//input[#name='login']");
if ($loginButton) {
$loginButton->click();
} else {
$this->fail('FB login button not found');
}
}
You have defined ur xpath expression incorrectly
it should have been //input[#id='email'] ##Note the # sign (not sure you need to escape '#' with a blackslash)
But none the less try changing how you try to locate your web elements
// try locating by LocatorStrategy id
$emailField = $this->webdriver->findElementBy(LocatorStrategy::id, 'email');
if ($emailField) {
$emailField->sendKeys(array($this->fbUser->email));
$emailField->submit();
} else {
$this->fail('FB login email field not found');
}
It is quicker to search by id, name as compared to xpath.
make the same changes for password field as well, like so
$passwordField = $this->webdriver->findElementBy(LocatorStrategy::id, "pass");
Related
For about a month, I have been trying to figure out why my code will not return anything after posting a wwwForm (I have also tried the newer equivalent of this function but I had no luck with that either.) The nameField and passwordField are taken from text boxes within the game and the code used in my login script is copied and pasted from a Register script but I have changed the file location to the login.php file. The register script works fine and I can add new users to my database but the login script only outputs "Form Sent." and not the "present" that should return when the form is returned and it never gets any further than that point meaning that it lets the user through with no consequence if they use an invalid name because the script never returns an answer. What should I do to fix this?
Thanks,
Unity Code:
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class Login : MonoBehaviour
{
public InputField nameField;
public InputField passwordField;
public Button acceptSubmissionButton;
public void CallLogInCoroutine()
{
StartCoroutine(LogIn());
}
IEnumerator LogIn()
{
WWWForm form = new WWWForm();
form.AddField("username", nameField.text);
form.AddField("password", passwordField.text);
WWW www = new WWW("http://localhost/sqlconnect/login.php", form);
Debug.Log("Form Sent.");
yield return www;
Debug.Log("Present");
if (www.text[0] == '0')
{
Debug.Log("Present2");
DatabaseManager.username = nameField.text;
DatabaseManager.score = int.Parse(www.text.Split('\t')[1]);
Debug.Log("Log In Success.");
}
else
{
Debug.Log("User Login Failed. Error #" + www.text);
}
}
public void Validation()
{
acceptSubmissionButton.interactable = nameField.text.Length >= 7 && passwordField.text.Length >= 8;
}
}
login.php:
<?php
echo "Test String2";
$con = mysqli_connect('localhost', 'root', 'root', 'computer science coursework');
// check for successful connection.
if (mysqli_connect_errno())
{
echo "1: Connection failed"; // Error code #1 - connection failed.
exit();
}
$username = mysqli_escape_string($con, $_POST["username"]);
$usernameClean = filter_var($username, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
$password = $_POST["password"];
if($username != $usernameClean)
{
echo "7: Illegal Username, Potential SQL Injection Query. Access Denied.";
exit();
}
// check for if the name already exists.
$namecheckquery = "SELECT username, salt, hash, score FROM players WHERE username='" . $usernameClean . "';";
$namecheck = mysqli_query($con, $namecheckquery) or die("2: Name check query failed"); // Error code # 2 - name check query failed.
if (mysqli_num_rows($namecheck) != 1)
{
echo "5: No User With Your Log In Details Were Found Or More Than One User With Your Log In Details Were Found"; // Error code #5 - other than 1 user found with login details
exit();
}
// get login info from query
$existinginfo = mysqli_fetch_assoc($namecheck);
$salt = $existinginfo["salt"];
$hash = $existinginfo["hash"];
$loginhash = crypt($password, $salt);
if ($hash != $loginhash)
{
echo "6: Incorrect Password"; // error code #6 - password does not hash to match table
exit;
}
echo "Test String2";
echo"0\t" . $existinginfo["score"];
?>
This problem was solved by changing the IENumerator LogIn() to IENumerator Start(). The program ran correctly when it was started at the beginning of a scene but not when it was triggered by a button being pressed. Weirdly, the Register() function in another script (which shares a lot of its code with this one) ran fine when triggered from a button. I'm not sure why this is.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class Login : MonoBehaviour
{
public InputField nameField;
public InputField passwordField;
public Button acceptSubmissionButton;
IEnumerator Start()
{
WWWForm form = new WWWForm();
form.AddField("username", nameField.text);
form.AddField("password", passwordField.text);
WWW www = new WWW("http://localhost/sqlconnect/login.php", form);
Debug.Log("Form Sent.");
yield return www;
Debug.Log("Present");
if (www.text[0] == '0')
{
Debug.Log("Present2");
DatabaseManager.username = nameField.text;
DatabaseManager.score = int.Parse(www.text.Split('\t')[1]);
Debug.Log("Log In Success.");
}
else
{
Debug.Log("User Login Failed. Error #" + www.text);
}
}
public void Validation()
{
acceptSubmissionButton.interactable = nameField.text.Length >= 7 && passwordField.text.Length >= 8;
}
}
I'm having some issues with a request from my boss.
I'm using the http://www.html-form-guide.com/ Registration forms he has created for use (I've attached the link just in case anyone want to use or look at it)
So I'm pretty new to PHP, but I've been gaining a crazy amount of knowledge.
Here is my problem - I need to make this form Register the user than Login Automatically. (This form has a Email confirmation system)
So I've managed to bypass the Email Confirmation and get the user to register, but I can't seem to figure out how to get auto login.
Here is what I've traced in the code:
function RegisterUser()
{
if(!isset($_POST['submitted']))
{
return false;
}
$formvars = array();
if(!$this->ValidateRegistrationSubmission())
{
return false;
}
$this->CollectRegistrationSubmission($formvars);
if(!$this->SaveToDatabase($formvars))
{
return false;
}
/*if(!$this->SendUserConfirmationEmail($formvars))
{
return false;
}*/
$this->SendAdminIntimationEmail($formvars);
$this->AutoLogin($formvars);// My call
return true;
}
This will pull in the name, email and password - put them in an array then send it off for validation and sanitation. I've placed a call function here.
After which I'll need to manually login with:
function Login()
{
if(empty($_POST['email']))
{
$this->HandleError("Email is empty!");
return false;
}
if(empty($_POST['password']))
{
$this->HandleError("Password is empty!");
return false;
}
$email = trim($_POST['email']);
$password = trim($_POST['password']);
if(!isset($_SESSION)){ session_start(); }
if(!$this->CheckLoginInDB($email,$password))
{
return false;
}
$_SESSION[$this->GetLoginSessionVar()] = $email;
return true;
}
So I took the last portion of the login function and made:
function AutoLogin(&$formvars)
{
$email = trim($formvars['email']);
$password = trim($formvars['password']);
if(!isset($_SESSION)){ session_start(); }
if(!$this->CheckLoginInDB($email,$password))
{
return false;
}
$_SESSION[$this->GetLoginSessionVar()] = $email;
return true;
}
I did an echo $email; echo $password; exit; test and I can see that the email and password are appearing. But the "Session" (I think) is not starting or the Check Login is not getting the data.
function CheckLogin()
{
if(!isset($_SESSION)){ session_start(); }
$sessionvar = $this->GetLoginSessionVar();
if(empty($_SESSION[$sessionvar]))
{
return false;
}
return true;
}
Now I see the is a CheckLoginInDB which is:
function CheckLoginInDB($email,$password)
{
if(!$this->DBLogin())
{
$this->HandleError("Database login failed!");
return false;
}
$email = $this->SanitizeForSQL($email);
$pwdmd5 = md5($password);
$qry = "Select name, email, pagecode, welcome from $this->tablename where email='$email' and password='$pwdmd5' and confirmcode='y'";
$result = mysql_query($qry,$this->connection);
if(!$result || mysql_num_rows($result) <= 0)
{
$this->HandleError("Error logging in. The email or password does not match");
return false;
}
$row = mysql_fetch_assoc($result);
$_SESSION['name_of_user'] = $row['name'];
$_SESSION['email_of_user'] = $row['email'];
$_SESSION['pagecode_of_user'] = $row['pagecode'];
$_SESSION['welcome_user'] = $row['welcome'];
return true;
}
What I can gather from this, its just a standard checking the database to see if this user exists and returning the results.
I've searching through stackoverflow and can't seem to see an answer to my problem.
I looked into Cookies, but I don't think that is something I really need here.
My questions are:
How can I make this bad boy start the session on registration?
Is my thinking on calling the AutoLogin(&$formvars) the right idea?
Have I gone wrong with this AutoLogin function syntax?
Just in case here is the GetLoginSessionVar():
function GetLoginSessionVar()
{
$retvar = md5($this->rand_key);
$retvar = 'usr_'.substr($retvar,0,10);
return $retvar;
}
It's a pity I can't attached the file I'm working on, but if you need any further code snippets let me know and I'll be sure to Edit this straight away!
But the "Session" (I think) is not starting or the Check Login is not
getting the data.
Is my thinking on calling the AutoLogin(&$formvars) the right idea?
Have I gone wrong with this AutoLogin function syntax?
It's not something wrong with the syntax, otherwise the code wouldn't even be compiled. Nevertheless I believe it's not the right idea.
You need to understand what's the problem before trying to fix it.
Debug the code. Use xdebug. If it's installed and active, you can use IDEs (e.g.: Visual Studio Code) to easily debug the code. Add breakpoints where you suspect there's something wrong.
If you don't want to use xdebug, you can add temporarily echoes or var_dumps to check if some areas of the code are processed and check some relevant values.
Also enable all errors reports and use a logger.
If the session is started after any output, there should be some warning.
Handle the errors and throw exceptions.
http://php.net/manual/en/function.error-log.php
http://php.net/manual/en/function.syslog.php
https://jtreminio.com/2012/07/xdebug-and-you-why-you-should-be-using-a-real-debugger/
session_start() works after output being sent
http://php.net/manual/en/function.error-reporting.php
You don't need to use the & in AutoLogin(&$formvars) if you're not changing the argument $formvars (you're just reading it).
You don't need to set session variables with all the user data. Create some structure (a class, an array, ...) with the user data outside those function and change those. AutoLogin should update that structure, something like this:
<?php
if (!$_SESSION) {
session_start();
}
$currentUser = array();
function getUserFromID($userID)
{
//TODO implement function
return $user;
}
function AutoLogin()
{
global $currentUser;
if(!empty($_SESSION['userID'])) {
return false;
}
$user = getUserFromID($_SESSION['userID']);
if (empty($user)) {
return false;
}
$currentUser = $user;
return true;
}
Maybe the session is not initialised before CheckLoginInDB is invoked (make var_dump($_SESSION); to check it). Use the $_SESSION only to save the user ID (or email) and read it to retrieve the user data.
I want to add user logins and logout/session expiration info into database, Its easy for normal login and logout, but I couldn’t figure out how to proceed with automatic session expirations.
My authentication works like below.
My Login controller action
if ($request->isPost()) {
$data = $request->getParams();
$userModel = new Application_Model_User_DbTable();
if ($user = $userModel->login($data['email'], $data['password'])) {
/* check if user is activated or not */
if ($user['status'] == 0) {
$this->view->loginerror = "<b>Account not active :</b> Please wait for admin to activate your account";
}elseif($user['status'] == -1){
$this->view->loginerror = "<b>Account Suspended :</b> Your account is suspeneded you must contact admin to continue";
}else {
/* Store authentication data in session */
$auth = Zend_Auth::getInstance();
$identity = Zend_Auth::getInstance()->getStorage();
$identity->write($user);
$this->_redirect('/fax');
}
} else {
$this->view->loginerror = "<b>Invalid login :</b> Email or passsword is invalid !";
}
}
Authenticate Method in my user control
function authenticate($email, $password) {
$where = array();
$where[] = $this->getAdapter()->quoteinto('email = ?', $email);
$user = $this->fetchRow($where);
if (isset($user['email'])) {
$salt = $user['password_salt'];
if (sha1($password . $salt) == $user['password']) {
/** here i will add login session info**/
return $user;
}
return false;
}
return false;
}
I am afraid that there is no core PHP or Zend function to perform this, session timeout doesn't run in the background. Unless timed out session makes another request, it is not even possible to know if session is timed out.
One of the method would be to make ajax request to a action to check for time outs and update your db in that action.
I've recently started using Zend Framework and I'm still pretty used to session_start, and assigning variables to certain session names (ie: $_SESSION['username'] == $username)
I'm trying to figure out how to do something similar to this in Zend. Right now, my auth script checks the credentials using LDAP against my AD server and, if successful, authenticates the user.
I want to create a script that will allow an admin user to easily "enter" someone else's session. Let's say admin1 had an active session and wanted to switch into user1's session. Normally I would just change the $_SESSION['username'] variable and effectively change the identity of the user logged in.
But with Zend, I'm not quite sure how to change the session info. For what it's worth, here's my authentication script:
class LoginController extends Zend_Controller_Action
{
public function getForm()
{
return new LoginForm(array(
'action' => '/login/process',
'method' => 'post',
));
}
public function getAuthAdapter(array $params)
{
$username = $params['username'];
$password = $params['password'];
$auth = Zend_Auth::getInstance();
require_once 'Zend/Config/Ini.php';
$config = new Zend_Config_Ini('../application/configs/application.ini', 'production');
$log_path = $config->ldap->log_path;
$options = $config->ldap->toArray();
unset($options['log_path']);
require_once 'Zend/Auth/Adapter/Ldap.php';
$adapter = new Zend_Auth_Adapter_Ldap($options, $username, $password);
$result = $auth->authenticate($adapter);
if ($log_path) {
$messages = $result->getMessages();
require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Log/Filter/Priority.php';
$logger = new Zend_Log();
$logger->addWriter(new Zend_Log_Writer_Stream($log_path));
$filter = new Zend_Log_Filter_Priority(Zend_Log::DEBUG);
$logger->addFilter($filter);
foreach ($messages as $i => $message) {
if ($i-- > 1) { // $messages[2] and up are log messages
$message = str_replace("\n", "\n ", $message);
$logger->log("Ldap: $i: $message", Zend_Log::DEBUG);
}
}
}
return $adapter;
}
public function preDispatch()
{
if (Zend_Auth::getInstance()->hasIdentity()) {
// If the user is logged in, we don't want to show the login form;
// however, the logout action should still be available
if ('logout' != $this->getRequest()->getActionName()) {
$this->_helper->redirector('index', 'index');
}
} else {
// If they aren't, they can't logout, so that action should
// redirect to the login form
if ('logout' == $this->getRequest()->getActionName()) {
$this->_helper->redirector('index');
}
}
}
public function indexAction()
{
$this->view->form = $this->getForm();
}
public function processAction()
{
$request = $this->getRequest();
// Check if we have a POST request
if (!$request->isPost()) {
return $this->_helper->redirector('index');
}
// Get our form and validate it
$form = $this->getForm();
if (!$form->isValid($request->getPost())) {
// Invalid entries
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
// Get our authentication adapter and check credentials
$adapter = $this->getAuthAdapter($form->getValues());
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if (!$result->isValid()) {
// Invalid credentials
$form->setDescription('Invalid credentials provided');
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
// We're authenticated! Redirect to the home page
$this->_helper->redirector('index', 'index');
}
public function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$this->_helper->redirector('index'); // back to login page
}
}
Is there any way to do what I have described? Thanks for any suggestions.
Given your code, the result of authenticating is stored in the PHP session through a Zend_Auth_Storage_Session object.
Calling Zend_Auth::getIdentity() gets access to the storage and returns the result if it is not empty. Likewise, you can change the stored identity by getting access to the underlying storage and changing its value. The actual identity stored as a result of authenticating with Zend_Auth_Adapter_Ldap is just a string value representing the LDAP username.
To effectively change the logged in user, you can do:
Zend_Auth::getInstance()->getStorage()->write('newUserName');
This assumes the default behavior which should be in place given your code.
What I do in my applications after successful authentication is to create a new object of some User model, and write that to the Zend_Auth session so that I have more information about the user available in each session, so you should be aware that different things can be in the storage depending on the application.
This is what I do for example:
$auth = new Zend_Auth(...);
$authResult = $auth->authenticate();
if ($authResult->isValid() == true) {
$userobj = new Application_Model_UserSession();
// populate $userobj with much information about the user
$auth->getStorage()->write($userobj);
}
Now anywhere in my application I call Zend_Auth::getInstance()->getIdentity() I get back the Application_Model_UserSession object rather than a string; but I digress.
The information that should help you is:
$user = Zend_Auth::getInstance()->getIdentity(); // reads from auth->getStorage()
Zend_Auth::getInstance()->getStorage()->write($newUser);
I am developing an ASP.NET Web Service:
# MyWS.cs
[WebMethod(EnableSession = true)]
public bool TryLogin(string user, string pass)
{
if (/* user validation is successful*/)
{
Context.Session["user"] = user;
return true;
}
else
{
Context.Session.Abandon();
return false;
}
}
[WebMethod(EnableSession = true)]
public string RetrieveSomething()
{
if (Context.Session["user"] == null)
throw Exception("User hasn't logged in.");
/* retrieve something */;
}
This ASP.NET must be consumed by a PHP Web site I am developing as well:
# ws_client.php
function get_soap_client()
{
if (!isset($_SESSION['client']))
$_SESSION['client'] = new SoapClient('MyWS.asmx?WSDL');
return $_SESSION['client'];
}
function try_login($user, $pass)
{
return get_soap_client()->TryLogin(
array('user' => $user, 'pass' => $pass))
->TryLoginResult;
}
function retrieve_something()
{
return get_soap_client()->RetrieveSomething(
array())->RetrieveSomethingResult;
}
# index.html
<?php
if (isset($_POST['submit']))
{
session_start();
require_once('ws_client.php');
if (try_login($_POST['user'],
$_POST['pass']))
{
session_write_close();
header('Location: /main.php');
exit();
}
?>
<html> <!-- login form here >
# main.php
<?php
session_start();
require_once('ws_client.php');
// Here I get the Exception "User hasn't logged in.", even though
// the user has logged in and the web service has already been notified.
echo htmlspecialchars(retrieve_something());
?>
What could be wrong with either my Web Service or my PHP site?
I don't know the PHP SOAP tools, but Session state is maintained through a cookie. Will this code accept a cookie the first time, then send it back on subsequent calls?