datatables / jquery / session variable / login form - datatables

Scenario:
1) login.php verifies the username and password and sets a session with the user id
$_SESSION['id'] = $id;
2) datatables / jquery calls an action.php file to fill the table with data
"ajax":{
url:"action.php",
type:"POST",
data:{action:'listTable'},
dataType:"json"
},
3) the action.php file calls the function listTable()
include('table.php');
$table = new table();
$table->listTable();
4) the listTable function (table.php) returns the whole data which is landing in the datatable
$output = array(
"draw" => intval($_POST["draw"]),
"recordsTotal" => $numRows,
"recordsFiltered" => $numRows,
"data" => $tableData
);
echo json_encode($output);
5) the data was selected with a sql command and was put into the tableData variable
Everything works fine in that case.
Ideas:
I wanted to acccess the session variable within the sql select command. This is not possible, because of the jquery / action.php api. Those are complete different files and have no access to that session. It is possible to set the user id within a hidden formular field, but this is not secure and easy to manipulate. A cookie file is also user editable. Furthermore Javascript cannot read server side session variables.
Question:
How can i use / access that php session variable in that scenario?
Update:
Thats not working too :/
overview.php
$userID = $_SESSION['id'];
data.js
"ajax":{
url:"action.php",
type:"POST",
data:{userID:userID, action:'listTable'},
dataType:"json"
},
table.php
$userID = $_POST["userID"];

I really don't understand here the problem, because of $_SESSION variable is available across the php files even if an ajax calls the php file. Until you call the AJAX call from the same browser and the ajax call includes the cookies (one of the cookie will be the session ID) then you will be able to reach your user ID in the SQL query on the PHP side, without passing to javascript/jquery your user's ID.
As you wrote in login.php:
$_SESSION['id'] = $id;
In your table.php where the SQL command is living:
echo isset($_SESSION['id']) ? "I'm existing!" : "I'm NOT existing!";
It will print "I'm existing!" if the user logged in and "I'm NOT existing!" if the user didn't log in. All the $_SESSION variables are available across the php files until the server gets the SESSION ID from the browser (which is a cookie). Cookies are automatically sent if you calling the same domain.

Related

Yii2 DbSession lost after redirect, 90% of the time

My login worked perfectly with PHP sessions. I tried switching to DbSession engine but login will not work anymore, as the session is empty after the page redirection.
Here's the workflow:
User enters his user id and clicks submit to post the data
Validation works (I tested) and a new identity cookie is created with the key sess = XXXX (tested with log just before redirect).
The $_SESSION is filled with the user data (tested with log just before redirect)
The page redirects with the new response cookie.
The password page loads and the request cookie has the same XXXX value (tested with log just after redirect + in chrome developer tools).
The session now only contains
[__flash] => Array
(
)
response cookie "sess" = request cookie "sess" = id in the session table, so the same key is everywhere, yet the session is still empty on the password page, 90% of the time (because in some random cases, the session is still there, but I can't reproduce it on demand)
I already checked these questions, not the same problem:
PHP session lost after redirect
Session lost after redirect in Codeigniter
Has anyone seen something similar before? I can't figure out what's causing this.
Addendas:
Session configuration
'session' => [
'class' => 'yii\web\DbSession',
'name' => 'sess',
'timeout' => 3600,
'db' => 'session_db',
'sessionTable' => 'session',
],
Session db config
$config['components']['session_db'] = [
'class' => 'yii\db\Connection',
...
],
Login action
// authenticate() Just checks if the user is valid, etc
Yii::$app->user->authenticate();
// login() just calls parent::login(), sets some session values then returns !$this->getIsGuest()
Yii::$app->user->login(Yii::$app->user);
update!! I have just noticed that if I use the same database instead of "db" (my main db) instead of "session_db", it works perfectly, even if both tables have exactly the same schema in the 2 databases.

How do you use Snap's authentication mechanisms during a single POST request?

I'm working on a Haskell Snap-based web app, and I want to expose an API endpoint that will be invoked by a remote service without establishing an authenticated session a-priori; however, I do want that request to be authenticated, so the credentials should be provided at the time of the request.
You could imagine the request containing four fields:
username
password
payload id
payload file
The payload id and file might be irrelevant for this question, but I include them because I (a) need to support file uploads in this request (which, as I understand it, restricts the encoding used to send fields) and (b) need to retrieve at least one non-file field. The combination of those things posed some difficulty when I set this up without authentication, so perhaps it is relevant.
In Snap parlance, let's call this handler uploadHandler.
As indicated above, I have this working fine without authentication, with a setup like this:
uploadHandler :: Handler App App ()
uploadHandler = do
-- collect files / form fields and process as needed.
-- and using the routes:
routes :: [(ByteString, Handler App App ())]
routes = [ ("/login", with auth handleLoginSubmit)
, ("/logout", with auth handleLogout)
, ("/new_user", with auth handleNewUser)
-- handle the upload:
, ("/upload", handleUpload)
]
The naive solution is to simply add 'with auth' and change the type of handleUpload:
uploadHandler :: Handler App (AuthManager App) ()
uploadHandler = do
-- collect files / form fields and process as needed.
-- and using the routes:
routes :: [(ByteString, Handler App App ())]
routes = [ ("/login", with auth handleLoginSubmit)
, ("/logout", with auth handleLogout)
, ("/new_user", with auth handleNewUser)
-- handle the upload, with auth:
, ("/upload", with auth handleUpload)
]
However, this seems to require two requests: (i) authenticate and establish a session, (ii) send the POST request containing the actual payload.
I found a way to do this in one request, but it seems like there should be a more elegant means. Here's the example restricted POST handler I've hacked together:
restrictedPOST :: Handler App (AuthManager App) ()
restrictedPOST = do
mName <- getPostParam "username"
mPass <- getPostParam "password"
let uName = C8.unpack $ fromMaybe "" mName
pass = ClearText $ fromMaybe "" mPass
authResult <- loginByUsername (T.pack uName) pass False
case authResult of
Left authFail -> writeText "Could not log in"
Right user -> writeText (T.append "Hello " (userLogin user))
Is there something like 'with auth' that I can use instead of turning this example (restrictedPOST) into a combinator? I realize it may need to know which fields to get credentials out of, but I also know very little about web services (maybe there is another means? Maybe this is a total non-issue, and I just don't know how to check auth for POST requests. I'm open to any suggestions!)
I don't think you understand what with auth is doing. It has nothing to do with whether authentication is required. All it does is convert a Handler b (AuthManager b) into a Handler b v. No permissions checks are performed. Your restrictedPOST function has the right idea.

How to run Behat tests as a logged-in user in Laravel?

I’m trying to create some tests for a Laravel application using Behat. Many of the tests require that a logged-in user sees specific data. My approach was this:
Log in as a user with a particular ID with Auth::loginUsingId($id)
Go to a particular URL in my app
Check that the content I’m expecting is there
Now, in my Behat context, although Auth::check() returns true, the filter I have set up doesn’t seem to see this. Auth::check() in that context returns false, and thus attempts to authenticate (via OAuth to the API my app talks to).
How can I go about testing my app as a logged-in user?
My Behat context file, in case it’s of help:
<?php
use Behat\Behat\Context\ClosuredContextInterface;
use Behat\Behat\Context\TranslatedContextInterface;
use Behat\Behat\Context\BehatContext;
use Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
/**
* Features context.
*/
class FeatureContext extends MinkContext
{
/**
* Laravel application instance.
*
* #var Illuminate\Foundation\Application
*/
protected $app;
/**
* #static
* #beforeSuite
*/
public static function bootstrapLaravel()
{
$unitTesting = true;
$testEnvironment = true;
$app = require_once __DIR__ . '/../../../../bootstrap/start.php';
$app->boot();
}
/**
* Initializes context.
* Every scenario gets its own context object.
*
* #param array $parameters context parameters (set them up through behat.yml)
*/
public function __construct(array $parameters)
{
}
/**
* #Given /^I am logged in as user ID (\d+)$/
*/
public function iAmLoggedInAsUserId($id)
{
Auth::loginUsingId($id);
}
}
And a sample test feature:
Feature: Sample Feature
Scenario: View groups a member is associated with
Given I am logged in as user ID 49
And I am on "/group"
Then I should see "Lorem Ipsum"
The problem you have is that the iAmLoggedInAsUserId method performs calls on the laravel framework directly, where as your subsequent instructions are browser/mink based. This is like having a PHP script which you run from the command line which sets (for its execution) the logged in user to 123, and then going to a web browser - user 123 wouldn't be logged in in that context.
You need to find a way for the code-based authentication to persist to your browser test.
Possible options:
An aggregate instruction for iAmLoggedInAsUserId which goes to the login page, and performs the login.
Hijack the session being used by mink, and update it to be logged in
A local-only work around which lets you, say, set a header to contain a user ID, and your code, only when running locally, uses this as the logged in user
Ideally, you should write a test to test logging in, however that is structured (i.e. option 1) and then re-use that for your tests which require a logged in user. The other two options are simply ideas if you use-case doesn't permit the first.
Edit: The following is an example aggregate instruction, this particular version requires the user exist in the system. You could however force a user to exist before hand, and if it was added as part of the test, delete it once the test is completed using the #AfterFeature hook:
/**
* #Given /I am logged in with the username "([^"]*)" and password "([^"]*)"/
*/
public function loginWithEmailAndPassword($username, $password)
{
//$this->ensureUserExistsWithEmailAndPassword($email, $password);
return array(
new Behat\Behat\Context\Step\Given("I am on \"/login\""),
new Behat\Behat\Context\Step\When("I fill in \"login_username\" with \"$username\""),
new Behat\Behat\Context\Step\When("I fill in \"login_password\" with \"$password\""),
new Behat\Behat\Context\Step\When("I press \"Login\""),
new Behat\Behat\Context\Step\Then("I should see \"Welcome\"")
);
}
Behat is a separate process to the browser session that it is spawning, so logging in like that won't work.
What I normally do is describe the steps for logging in as a test user. Something like the following.
Given I am on the login page
And I fill in "username" with "testuser"
And I fill in "password" with "testpassword"
And I press "Log in"
Then I should be logged in as "testuser"
You can set that up as a background in behat that will run before each scenario.
I'd probably also reduce the steps down to a method in your FeatureContext that can be called in a single step, something like Given I am logged in as "testuser".

In coldfusion: how to remember that a user has logged in?

I have a username/password on a login page, which goes to 'member' page. Basically, I used a statement that finds the number of rows in a SQL query, and if it's not 1 (the correct username/password combination), then it redirects back to the login page.
However, on the 'member' page, there are forms to do various things, like add new rows to a table using SQL, or return queries of joined tables. These forms also link back to this 'member' page, but the conditions for logging in (which requires a username variable and password variable) would no longer be met.
So, how do I get the server to remember whether a user is logged on or not?
In the application.cfm or application.cfc, you will need to enable sessionManagement = true so you can start tracking variables across page requests. Then when a user logs in, you will set a variable like isAuthenticated = true. Only redirect to the login page if the isAuthenticated = false or undefined (you can set a default using cfparam in onSessionStart of Application.cfm)
Rough example, assuming you are using ColdFusion 9+
Application.cfc
component {
this.name = 'myApplication';
this.SessionManagement = true;
public function onSessionStart() {
param name='session.isAuthenticated' default=false;
}
}
checkAuthentication.cfm
<cfscript>
if (!session.isAuthenticated) {
// send to login page
}
</cfscript>
In your login processing page, make sure you set session.isAuthenticated to true, and then it should skip checking / asking for the login. You can also look into the built-in authentication functions of CFLOGIN.
Hope that helps a bit.

Using Twitter API on shared server - Rate limit exceeded even though I am caching the response

I have written a php script which gets the latest status update for 12 different twitter accounts by pulling an xml for each and caching it on my server. This currently runs every 30 minutes.
Unfortunately I keep getting the "Rate limit exceeded. Clients may not make more than 150 requests per hour." error event though i'm only making 24 requests from the 150 I should have.
I assume this is because my domain is on a shared server and twitter is counting other requests against me.
How can I authorise my requests so i'm not restriced by the standard IP limit?
I have no experience of OAuth so need step by step instructions if possible.
Thanks in advance!
OK so I managed to get the most of this working with no previous experience of API's etc.
Here is my step by step guide:
Step 1.
Create a Twitter list.
Go to: https://twitter.com/username/lists
Click "Create list"
Enter details and save.
Go to a twitter user you wish to add to the list and click the gear dropdown and select "Add or remove from lists". Tick the checkbox next to your list.
Step 2.
Create a Twitter App via: https://dev.twitter.com/apps/new
Log in using your Twitter credentials.
Give your app a name, description etc.
Go to the Settings tab and change the Access type to Read and Write then click "Update this Twitter application's settings".
Click "Create my access token" at the bottom of the page.
You will now have a Consumer Key, Consumer secret, Access token and Access token secret. Make a note of these.
Step 3. Create API tokens.
Download and install onto your server the Abraham Twitter oAuth library from: https://github.com/abraham/twitteroauth (I'll use a folder called "twitter").
Create a new file, name it authorise.php in the oAuth folder and put the following code inside (with your generated keys in place of the named text). (Put the code between < ? PHP and ?> brackets).
// Create our twitter API object
require_once("twitteroauth/twitteroauth.php");
$oauth = new TwitterOAuth('Put-Consumer-Key-here', 'Put-Consumer-secret-here',
'Put-Access-Token-here', 'Put-Access-token-secret-here');
// Send an API request to verify credentials
$credentials = $oauth->get("account/verify_credentials");
echo "Connected as #" . $credentials->screen_name;
// Post our new "hello world" status
$oauth->post('statuses/update', array('status' => "hello world"));
This has now authorised your twitter App for the API and posted a "hello world" status on your twitter account.
Note: The Read / Write access change we did earlier alowed the code to post the status update, it's not actually needed to pull the list from the API but I did it to make sure it was working OK. (You can turn this off again by going back to the Settings).
Step 4.
Create PHP file to pull your list and cache the file.
Create an XML file (YOUR-FILE-NAME.xml) and save it in the oAuth folder.
Create a PHP file (YOUR-PHP-FILE.php) and save it in the oAuth folder
Edit the below code with your twitter API keys, file name and twitter list details and save it in your PHP file. (Put the code within < ? PHP and ?> brackets).
/* Twitter keys & secrets here */
$consumer_key = 'INSERT HERE';
$consumer_secret = 'INSERT HERE';
$access_token = 'INSERT HERE';
$access_token_secret = 'INSERT HERE';
// Create Twitter API object
require_once('twitteroauth/twitteroauth.php');
// get access token and secret from Twitter
$oauth = new TwitterOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret);
// fake a user agent to have higher rate limit
$oauth->useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9';
// Send an API request to verify credentials
$credentials = $oauth->get('account/verify_credentials');
echo 'Connected as #' . $credentials->screen_name . '\n';
// Show API hits remaining
$remaining = $oauth->get('account/rate_limit_status');
echo "Current API hits remaining: {$remaining->remaining_hits}.\n";
$ch = curl_init();
$file = fopen("YOUR-FILE-NAME.xml", "w+");
curl_setopt($ch, CURLOPT_URL,'https://api.twitter.com/1/lists/statuses.xml?slug=INSERT-LIST-NAME&owner_screen_name=INSERT-YOUR-TWITTER-USERNAME-HERE&include_entities=true');
curl_setopt($ch, CURLOPT_FILE, $file);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($file);?>
Copy the file path into your browser and test it. (e.g. http://www.yourwebsite.com/twitter/YOUR-PHP-FILE.php)
This should contact twitter, pull the list as an XMl file and save it into YOUR-FILE-NAME.xml. Test it by opening the XML file, it should have the latest statuses from the users in your twitter list.
Step 5.
Automate the PHP script to run as often as you like (up to 350 times per hour) via a Cron job.
Open your Cpanel and click "Cron jobs" (usually under Advanced).
You can choose the regularity of your script using the common settings.
In the command field add the following code:
php /home/CPANEL-USERNAME/public_html/WEBSITE/twitter/YOUR-PHP-FILE.php >/dev/null 2>&1
Your script will now run as often as you have chosen, pull the list from twitter and save it into YOUR-FILE-NAME.xml.
Step 6.
You can now pull statuses from the cached XML file meaning your visitors will not be making unnecessary calls to the API.
I've not worked out how to target a specific screen_name yet if anyone can help there?
a) don't check 12 different accounts, create a [public] list https://twitter.com/lists and check only the it => 12 times less requests
b) use this awesome oAuth lib: https://github.com/abraham/twitteroauth and use oAuth requests instead of unsigned => you will get 350 requests and they will not be affected by IP limit