how to create session in php with mongodb - php

in node.js i will create mongodb session like this.
app.configure(function()
{
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({
secret: 'MY SECRET',
store: new MongoStore({
db: 'MY SESSION DB',
host: 'localhost',
port:88888
})
}));
app.use(everyauth.middleware());
app.use(express.methodOverride());
app.use(app.router);
});
how to create session with mongodb in php.i am new one for php..i want to create session with mongodb in php(webserver: apache),so let me know how to create

You must use a session handler to accomplish this. Normally we don't answer these sort of questions which lack research of any kind but, this one time, here is a small, extremely simple, self contained edition I have:
class Session{
public $db;
/**
* This decides the lifetime (in seconds) of the session
*
* #access private
* #var int
*/
public $life_time='+2 weeks';
/**
* This stores the found session collection so that we don't
* waste resources by constantly going back for it
*
* #access private
* #var sessions
*/
private $_session = array();
/**
* Constructor
*/
function open() {
// Ensure index on Session ID
$this->db->sessions->ensureIndex(array('session_id' => 1), array("unique" => true));
// Register this object as the session handler
session_set_save_handler(
array( $this, "openSession" ),
array( $this, "closeSession" ),
array( $this, "readSession" ),
array( $this, "writeSession"),
array( $this, "destroySession"),
array( $this, "gcSession" )
);
session_start(); // Start the damn session
}
/**
* Open session
*
* This function opens a session from a save path.
* The save path can be changed the method of opening also can
* but we do not change that we just do the basics and return
*
* #param string $save_path
* #param string $session_name
*/
function openSession( $save_path, $session_name ) {
global $sess_save_path;
$sess_save_path = $save_path;
// Don't need to do anything. Just return TRUE.
return true;
}
/**
* This function closes the session (end of session)
*/
function closeSession() {
// Return true to indicate session closed
return true;
}
/**
* This is the read function that is called when we open a session.
* This function attempts to find a session from the Db. If it cannot then
* the session class variable will remain null.
*
* #param string $id
*/
function readSession( $id ) {
// Set empty result
$data = '';
// Fetch session data from the selected database
$time = time();
$this->_sessions = $this->db->sessions->findOne(array("session_id"=>$id));
if (!empty($this->_sessions)) {
$data = $this->_sessions['session_data'];
}
return $data;
}
/**
* This is the write function. It is called when the session closes and
* writes all new data to the Db. It will do two actions depending on whether or not
* a session already exists. If the session does exist it will just update the session
* otherwise it will insert a new session.
*
* #param string $id
* #param mixed $data
*
* #todo Need to make this function aware of other users since php sessions are not always unique maybe delete all old sessions.
*/
function writeSession( $id, $data ) {
//Write details to session table
$time = strtotime('+2 weeks');
// If the user is logged in record their uid
$uid = $_SESSION['logged'] ? $_SESSION['uid'] : 0;
$fields = array(
"session_id"=>$id,
"user_id"=>$uid,
"session_data"=>$data,
"expires"=>$time,
"active"=>1
);
$fg = $this->db->sessions->update(array("session_id"=>$id), array('$set'=>$fields), array("upsert"=>true));
// DONE
return true;
}
/**
* This function is called when a user calls session_destroy(). It
* kills the session and removes it.
*
* #param string $id
*/
function destroySession( $id ) {
// Remove from Db
$this->db->sessions->remove(array("session_id" => $id), true);
return true;
}
/**
* This function GCs (Garbage Collection) all old and out of date sessions
* which still exist in the Db. It will remove by comparing the current to the time of
* expiring on the session record.
*
* #todo Make a cronjob to delete all sessions after about a day old and are still inactive
*/
function gcSession() {
$this->db->sessions->remove(array('expires' => array('$lt' => strtotime($this->life_time))));
return true;
}
}
Which can be called like so:
$session = new Session;
$session->db=$mongo->my_db;
$session->open();
It is a very basic example of how to do this.
Afterwards you can just use it like a normal session like so:
$_SESSION['user_id'] = $id;

Related

Dynamically display AWS Cognito in PHP & Twig

I am building out an interface to create a self-serve way for people to manage their Cognito IDP, but am struggling to pull through the actual data. I know I've got the foundations here but there's something I'm missing. I really need a little direction to help me get back on track.
Where it's failing is in the onPreSetData but I feel like I'm not 100% on all of the code, it's passing null through within the onPreSetData in my loginSettingsType on:
$cognitoAPIFacade = new CognitoAPIFacade();
To try work around this I was hard coding the variables but not going too well haha
it takes a bit of following but I'm certain I'm 99% there and just need a little help to get fully over the line
I'm not the most experienced coder so this is all new learning for me, any help you can provide is hugely appreciated.
I've tried adding comments to the code where it felt good to, I can clarify any areas as needed
My controller is below:
public function editAction(string $customerId, Admin $user, Request $request): Response
{
$customer = $this->validateCustomer($customerId, $user);
$twoFactorOmniLoginFacade = new TwoFactorOmniLoginFacade();
$cognitoAPIFacade = new CognitoAPIFacade();
$cognitoCustomerUserpoolIDP = new CognitoCustomerUserPool();
$formData = new LoginSettingsFormData($customer, $twoFactorOmniLoginFacade, $cognitoAPIFacade, $cognitoCustomerUserpoolIDP);
$form = $this->createForm(LoginSettingsType::class, $formData);
$form->handleRequest($request);
return $this->render('customer/login_settings.twig', [
'title' => $customer->getName(),
'javascript_action' => $request->attributes->get('_route'),
'form' => $form->createView(),
'page' => 'customer_login_settings',
] + $this->getTemplateParams($user, $customer));
}
/**
* #Route("", name="customer_login_settings_put", methods={"PUT"})
*
* #param Request $request
* #param Admin $user
* #param string $customerId
*
* #return Response
*
* #throws Exception
*/
public function putAction(Request $request, Admin $user, string $customerId): Response
{
$customer = $this->validateCustomer($customerId, $user);
$customerCognitoSettingsRepo = $this->getManager()->getRepository(CognitoCustomerUserPool::class);
$userPool = $customerCognitoSettingsRepo->getByCustomerID($customer->getCustomerUuid());
$twoFactorOmniLoginFacade = new TwoFactorOmniLoginFacade();
$cognitoAPIFacade = new CognitoAPIFacade();
$cognitoCustomerUserpoolIDP = new CognitoCustomerUserPool();
$formData = new LoginSettingsFormData($customer, $twoFactorOmniLoginFacade, $cognitoAPIFacade, $cognitoCustomerUserpoolIDP);
$form = $this->createForm(LoginSettingsType::class, $formData);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$formData->saveChanges($customer);
$this->addFlash('success', 'Changes saved successfully');
} else {
$this->addFlash('error', 'There was an error saving changes');
}
return $this->redirectToRoute('customer_login_settings_edit', ['customer_id' => $customerId]);
}
Login settings type:
public function buildForm(FormBuilderInterface $builder, array $options) {
---
form $builder adds
---
$builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPreSetData']);
}
public function onPreSetData(FormEvent $event, LoggerInterface $logger): void
{
$twoFactorOmniLoginFacade = new TwoFactorOmniLoginFacade();
// Both of these below are returning empty when I use VSCode breakpoints
$cognitoAPIFacade = new CognitoAPIFacade();
$cognitoCustomerUserpoolIDP = new CognitoCustomerUserPool();
$formData = $event->getData();
$form = $event->getForm();
// TODO: I was working on this but unsure where to go from here
// $cognitoCustomerUserpoolIDP = $formData->getCognitoCustomerUserpoolIDP();
if (!$formData) {
return;
}
$idpsettings = $cognitoAPIFacade->getCognitoIDPSettings($logger, $cognitoCustomerUserpoolIDP);
$customer = $formData->getCustomer();
$form = $this->createForm(LoginSettingsType::class, $formData)
---
$Builder adds
---
}
Key script from Facade I made:
public function getCognitoIDPSettings(LoggerInterface $logger, string $cognitoCustomerUserpoolIDP): array
{
$body = json_encode([
'cognito_customer_user_pool_idp_id' => $cognitoCustomerUserpoolIDP,
]);
$url = COGNITO_API_INVOKE_URL . '/idp/get';
$response = $this->postFunction($logger, $url, $body, 'application/json');
$this->checkResponseStatus($response);
$body = json_decode($response->getContent(), true);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new JsonException("failed decoding response from cognito api");
}
return $body;
}
I separated out my form data as it was quite large:
class LoginSettingsFormData
{
private $customer;
private $twoFactorOmniLoginFacade;
private $cognitoAPIFacade;
private $cognitoCustomerUserpoolIDP;
private $ignoreUsageDeactivate;
private $twoFactorLoginApplication;
private $whitelistedIps = [];
/**
* Constructor for the class.
*
* #param CustomerEntity $customer Customer Entity object
* #param TwoFactorOmniLoginFacade $twoFactorOmniLoginFacade Two-factor omni login facade object
* #param CognitoAPIFacade $cognitoAPIFacade Cognito API facade object
* #param CognitoCustomerUserPool $cognitoCustomerUserpoolIDP Cognito customer user pool object
*/
public function __construct(
CustomerEntity $customer,
TwoFactorOmniLoginFacade $twoFactorOmniLoginFacade,
CognitoAPIFacade $cognitoAPIFacade,
CognitoCustomerUserPool $cognitoCustomerUserpoolIDP)
{
$this->customer = $customer;
$this->twoFactorOmniLoginFacade = $twoFactorOmniLoginFacade;
$this->cognitoAPIFacade = $cognitoAPIFacade;
$this->cognitoCustomerUserpoolIDP = $cognitoCustomerUserpoolIDP;
$this->ignoreUsageDeactivate = $customer->getLoginSettings()->isIgnoreUsageDeactivate();
$this->twoFactorLoginApplication = CustomerSetting::findByCustomerUuid($customer->getId())->twoFactorLoginApplication;
$this->whitelistedIps = $customer->getLoginWhitelist();
}
/**
* Submit changes made to the loggin settings
*
* #param array $formData Array of form data
* #param LoggerInterface $logger Logger to log errors
*/
public function submit(array $formData, LoggerInterface $logger)
{
// Check if idp is set
if (null !== $this->idp && 'none' === $formData['idp_type']) {
// Delete the Cognito IDP settings with the provided logger and idp
$this->cognitoAPIFacade->deleteCognitoIDPSettings($logger, $this->idp);
// exit early
return;
}
if ('none' !== $formData['idp_type']) {
// Create a user pool with the provided form data and logger
// TODO:: consider handle exceptions?
$this->cognitoAPIFacade->createUserPool($logger, $formData);
}
}
/**
* Update the identity provider for the loggin settings
*
* #param CognitoAPIFacade $cognitoAPIFacade
* #param CognitoAPIFacade $userpool
* #param Logger $logger Logger to log errors
*/
public function updateIdentityProvider(CognitoAPIFacade $cognitoAPIFacade, $userpool, Logger $logger): void
{
// Assign the user pool value from the cognitoAPIFacade
$userPool = $cognitoAPIFacade->getUserPool();
// log error message if the user pool is null
if (null === $userPool) {
$logger->error("User pool is null, please check the configuration");
return;
}
// Store provider for quick reference
$idp = $userPool->getIdentityProvider();
// If the selected identityprovider is null
if (null === $idp) {
// Check if the form data specifies to set the identityprovider to "none"
if ($this->idpType !== 'none' && $this->active) {
// Otherwise, create a new identityprovider using the form data
$cognitoAPIFacade->createUserPool($logger, $this);
}
return;
}
// Check if the form data specifies to set the identityprovider to "none"
if ($this->idpType === 'none') {
// If so, delete the existing identityprovider settings
$cognitoAPIFacade->deleteCognitoIDPSettings($logger, $idp);
return;
}
// If the form data specifies a different identity provider than the current one
if ($idp->getCognitoIDPSettings() !== $this->idpType
&& ($this->idpType === "SAML" || $this->idpType === "Google")
&& $idp->getCognitoIDPSettings() === "none") {
// Delete the existing identityprovider settings and create a new user pool using the form data
$cognitoAPIFacade->deleteCognitoIDPSettings($logger, $idp);
$cognitoAPIFacade->createUserPool($logger, $this);
}
}
/**
* Save changes made to the object to the database
*
* #return bool Returns true if the changes were saved successfully, false otherwise
*/
public function saveChanges(CustomerEntity $customer)
{
// Set the ignore usage deactivate flag in the customer's login settings
$this->customer->getLoginSettings()->setIgnoreUsageDeactivate($this->ignoreUsageDeactivate);
// Clear the current whitelist of IP addresses for the customer's login settings
$this->customer->getLoginWhitelist()->clear();
// Add each IP address to the whitelist in the customer's login settings
foreach ($this->whitelistedIps as $ip) {
$this->customer->getLoginWhitelist()->add($ip);
}
// Save the changes to the customer object
$this->customer->save();
}
/**
* Set whether to ignore usage deactivation
*
* #param bool $ignoreUsageDeactivate true to ignore usage deactivation, false otherwise
*/
public function setIgnoreUsageDeactivate(bool $ignoreUsageDeactivate)
{
$this->ignoreUsageDeactivate = $ignoreUsageDeactivate;
}
/**
* Bool check whether usage deactivation is ignored
*
* #return bool true if usage deactivation is ignored, false otherwise
*/
public function isIgnoreUsageDeactivate(): bool
{
return $this->ignoreUsageDeactivate;
}
/**
* Set the two factor login application
*
* #param mixed $twoFactorLoginApplication the two factor login application
*/
public function setTwoFactorLoginApplication($twoFactorLoginApplication)
{
$this->twoFactorLoginApplication = $twoFactorLoginApplication;
}
/**
* Get the two factor login application
*
* #return mixed the two factor login application
*/
public function getTwoFactorLoginApplication()
{
return $this->twoFactorLoginApplication;
}
/**
* Set the customer entity
*
* #param CustomerEntity $customer the customer entity
*/
public function setCustomer(CustomerEntity $customer)
{
$this->customer = $customer;
}
/**
* Get the customer entity
*
* #return CustomerEntity the customer entity
*/
public function getCustomer(): CustomerEntity
{
return $this->customer;
}
/**
* Set the whitelisted IP addresses
*
* #param array $whitelistedIps the array of whitelisted IP addresses
*/
public function setWhitelistedIps(array $whitelistedIps)
{
$this->whitelistedIps = $whitelistedIps;
}
/**
* Get the array of whitelisted IP addresses
*
* #return array the array of whitelisted IP addresses
*/
public function getWhitelistedIps(): array
{
return $this->whitelistedIps;
}
/**
* Get the two factor applications
*
* #return mixed the two factor applications
*/
public function getTwoFactorApplications()
{
return $this->twoFactorOmniLoginFacade->findTwoFactorApplications($this->customer);
}
/**
* Get the two factor login setting
*
* #return mixed the two factor login setting
*/
public function getTwoFactorLoginSetting()
{
return $this->twoFactorOmniLoginFacade->findTwoFactorLoginSetting($this->customer->getId());
}
/**
* Get the Cognito IDP settings
*
* #return mixed the Cognito IDP settings
*/
public function getCognitoIDPSettings()
{
return $this->cognitoAPIFacade->getCognitoIDPSettings($this->cognitoCustomerUserpoolIDP);
}

Symfony oauth2 provider, Remove repeating code by 'stashing' new class variables within

I'm working on a big project that implements OAuth2 -> Specifically Oauth2-EveOnline
im using
PHP 8
Symfony 6
EveLabs/Oauth2
and as part of usage when creating the provider class the following code is required
$provider = new EveOnline([
'clientId' => $this->getParameter('eve.client_id'),
'clientSecret' => $this->getParameter('eve.client_secret'),
'redirectUri' => $this->getParameter('eve.redirect_uri'),
]);
What I would like to do is 'stash' this array within the EveOnline provider class so that in future all I have to call is
$provider = new EveOnline();
Unfortunate searching for an answer hasn't been fruitful, perhaps my searches are too vague, my problem is something that nobody has tried before or more likely it's simple and I am not experienced enough to see the solution
I have tried using a Service class to setup the provider but calling $this-> gets a little messy and I have been unable to work that out as $this calls the document your working on and to get .env details it should be done from the controller
I know that if I copy the provider from vendor/src to my App/src then it will override the vendor/src, if I can make changes to include my array and pull the info it needs then I can save myself passing the array on almost every page as I require the provider to manage tokens and communication with the EVE API (ESI).
Is this possible or is there a way I can move this to a Service?
When I learned how to create services I was rather happy and have created a few to handle all my API calls I just pass in my $provider and return the $response to my controller.
I was hoping to do something similar with building the provider, create a page builder service that requires 2 lines in the controller and handles setting up the provider and populating the session with info that I need to call on my pages, or use in DB/API queries.
I have already implemented a service for refreshing tokens but that is a very small inefficient feat.
App\Controller
<?php
namespace App\Controller;
use App\Service\TokenRefresh;
use Evelabs\OAuth2\Client\Provider\EveOnline;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
class Controller extends AbstractController
{
#[Route('/', name: 'page_homepage')]
public function homepage()
{
$session = new Session();
$session->start();
$provider = new EveOnline([
'clientId' => $this->getParameter('eve.client_id'),
'clientSecret' => $this->getParameter('eve.client_secret'),
'redirectUri' => $this->getParameter('eve.redirect_uri'),
]);
if($_SESSION['token']->hasExpired()){
$token = new TokenRefresh();
$token->newToken($provider);
}
if (isset($_SESSION)) {
dump($_SESSION);
}
return $this->render('homepage.html.twig');
}
#[Route('/login', name: 'page_login')]
public function login()
{
$session = new Session();
$session->start();
$scopes = [
'scope' => ['publicData', 'esi-markets.read_character_orders.v1', 'esi-markets.structure_markets.v1'] // array or string
];
$provider = new EveOnline([
'clientId' => $this->getParameter('eve.client_id'),
'clientSecret' => $this->getParameter('eve.client_secret'),
'redirectUri' => $this->getParameter('eve.redirect_uri'),
]);
if (!isset($_GET['code'])) {
// here we can set requested scopes but it is totally optional
// make sure you have them enabled on your app page at
// https://developers.eveonline.com/applications/
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl($scopes);
$_SESSION['oauth2state'] = $provider->getState();
unset($_SESSION['token']);
header('Location: ' . $authUrl);
exit;
// Check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
} else {
// In this example we use php native $_SESSION as data store
if (!isset($_SESSION['token'])) {
// Try to get an access token (using the authorization code grant)
$_SESSION['token'] = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
} elseif ($_SESSION['token']->hasExpired()) {
$token = new TokenRefresh();
$token->newToken($provider);
}
// Optional: Now you have a token you can look up a users profile data
try {
// We got an access token, let's now get the user's details
$user = $provider->getResourceOwner($_SESSION['token']);
// Use these details to create a new profile
} catch (\Exception $e) {
// Failed to get user details
exit('Oh dear...');
}
$user = $user->toArray();
$owner = [
'character_id' => $user['CharacterID'],
'character_name' => $user['CharacterName']
];
$session->set('owner', $owner);
$_SESSION['owner'] = $owner;
dump($user);
if (isset($_SESSION)) {
dump($_SESSION);
dump($session->get('owner'));
}
return $this->render('login.html.twig');
}
}
// TODO: Remove debug session clear
#[Route('/clrsession', name: 'app_clrsession')]
public function clrsession(){
$session = new Session();
$session->invalidate();
return $this->redirectToRoute('page_homepage');
}
}
Provider\EveOnline
<?php
namespace Evelabs\OAuth2\Client\Provider;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
use League\OAuth2\Client\Token\AccessToken;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use Psr\Http\Message\ResponseInterface;
class EveOnline extends AbstractProvider
{
use BearerAuthorizationTrait;
/**
* Default scopes
*
* #var array
*/
public $defaultScopes = [];
/**
* Get the string used to separate scopes.
*
* #return string
*/
protected function getScopeSeparator()
{
return ' ';
}
/**
* Returns the base URL for authorizing a client.
*
* Eg. https://oauth.service.com/authorize
*
* #return string
*/
public function getBaseAuthorizationUrl()
{
return 'https://login.eveonline.com/oauth/authorize';
}
/**
* Returns the base URL for requesting an access token.
*
* Eg. https://oauth.service.com/token
*
* #param array $params
* #return string
*/
public function getBaseAccessTokenUrl(array $params)
{
return 'https://login.eveonline.com/oauth/token';
}
/**
* Returns the URL for requesting the resource owner's details.
*
* #param AccessToken $token
* #return string
*/
public function getResourceOwnerDetailsUrl(AccessToken $token)
{
return 'https://login.eveonline.com/oauth/verify';
}
/**
* Returns the default scopes used by this provider.
*
* This should only be the scopes that are required to request the details
* of the resource owner, rather than all the available scopes.
*
* #return array
*/
protected function getDefaultScopes()
{
return $this->defaultScopes;
}
/**
* Checks a provider response for errors.
*
* #throws IdentityProviderException
* #param ResponseInterface $response
* #param array|string $data Parsed response data
* #return void
*/
protected function checkResponse(ResponseInterface $response, $data)
{
//not throwing anything for 2xx responses
if (intval(substr($response->getStatusCode(), 0, 1)) === 2) {
return;
}
$message = $this->safeRead($data, 'error_description') || $this->safeRead($data, 'message');
throw new IdentityProviderException(
$message ?: $response->getReasonPhrase(),
$response->getStatusCode(),
$response
);
}
/**
* Generates a resource owner object from a successful resource owner
* details request.
*
* #param array $response
* #param AccessToken $token
* #return ResourceOwnerInterface
*/
protected function createResourceOwner(array $response, AccessToken $token)
{
return new EveOnlineResourceOwner($response);
}
/**
* Internal helper function to safe read from an array
* #param mixed $array
* #param string|int $key
* #return null
*/
private function safeRead($array, $key)
{
return !empty($array[$key]) ? $array[$key] : null;
}
}

How can I use my session database with Zend's Session Manager?

When the Zend Session Manager from the tutorial starts a session, it generates a session key and posts a whole lot of data into the session. But I have a session system already set up with my own session keys and a different set of session data. How can I change the Zend configuration to use mine instead?
For reference, here is the Zend Session:
array (size=2)
'__ZF' =>
array (size=2)
'_REQUEST_ACCESS_TIME' => float 1468447555.1396
'_VALID' =>
array (size=3)
'Zend\Session\Validator\Id' => string 'xxxxxxxxxxxxxxxxxxxxxxxxxx' (length=26)
'Zend\Session\Validator\RemoteAddr' => string '--ip addr--' (length=13)
'Zend\Session\Validator\HttpUserAgent' => string '--user agent info--' (length=114)
'initialized' =>
object(Zend\Stdlib\ArrayObject)[371]
protected 'storage' =>
array (size=3)
'init' => int 1
'remoteAddr' => string '--ip addr--' (length=13)
'httpUserAgent' => string '--user agent info--' (length=114)
protected 'flag' => int 2
protected 'iteratorClass' => string 'ArrayIterator' (length=13)
protected 'protectedProperties' =>
array (size=4)
0 => string 'storage' (length=7)
1 => string 'flag' (length=4)
2 => string 'iteratorClass' (length=13)
3 => string 'protectedProperties' (length=19)
And here's what the session information I'm currently storing looks like (it's in a database, so I currently reference it with a Doctrine Entity):
object(MyModule\Entity\MySession)[550]
protected 'sessionid' => string 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' (length=40)
protected 'data1' => string 'xxxxx' (length=5)
protected 'data2' => string 'xxxxxxxxxxxx' (length=12)
protected 'datatime' =>
object(DateTime)[547]
public 'date' => string '2016-07-13 17:05:52.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'xxxxxxxxxxxxxxx' (length=15)
protected 'data3' => boolean false
protected 'data4' => string '' (length=0)
protected 'data5' => int 9
protected 'data6' => int 17765
protected 'data7' => boolean false
My code for the session manager comes from this SO answer, so I'm providing a link rather than repasting it and cluttering up this question.
The reason I want to use Zend Session Manager rather than simply referencing my stored session information with Doctrine is so that I have a layer between the my program and the stored session information - so then I can change the way I access the session information without having to change my entire program.
I ended up solving this on my own by extending the SessionManager, SessionStorage, and SessionSaveHandler classes and rewriting some of the functionality. I also changed the Module.php and module.config.php files. This is what the changes look like:
module.config.php
<?php
/* ...required use statements... */
return array(
'session' => array(
'config' => array(
'class' => 'Zend\Session\Config\SessionConfig',
'options' => array(
'name' => [my session name],
),
),
'storage' => 'MySession\Model\MySessionStorage',
'save_handler' => 'MySession\Model\MySessionSaveHandler'
),
'service_manager' => array(
'factories' => array(
'session_service' => function($serviceManager) {
$entityManager = $serviceManager->get('Doctrine\ORM\EntityManager');
return new SessionService($entityManager, 'MySession');
},
'MySession\Model\MySessionSaveHandler' => function($serviceManager) {
$sess = $serviceManager->get('onmysession_service');
/* #var $adapter \Zend\Db\Adapter\Adapter */
$adapter = $sm->get('Zend\Db\Adapter\Adapter');
$tableGateway = new TableGateway('mytablename', $adapter);
return new MySessionSaveHandler($tableGateway, new DbTableGatewayOptions(), $sess);
},
'MySessionManager' => function ($sm) {
$config = $sm->get('config');
if (isset($config['session'])) {
$session = $config['session'];
$sessionConfig = null;
if (isset($session['config'])) {
$class = isset($session['config']['class']) ? $session['config']['class'] : 'Zend\Session\Config\SessionConfig';
$options = isset($session['config']['options']) ? $session['config']['options'] : array();
$sessionConfig = new $class();
$sessionConfig->setOptions($options);
}
$sessionStorage = null;
if (isset($session['storage'])) {
$class = $session['storage'];
$sessionStorage = new $class();
}
$sessionSaveHandler = null;
if (isset($session['save_handler'])) {
// class should be fetched from service manager since it will require constructor arguments
$sessionSaveHandler = $sm->get($session['save_handler']);
}
$sessionManager = new MySessionManager($sessionConfig, $sessionStorage, $sessionSaveHandler);
} else {
$sessionManager = new MySessionManager();
}
MySession::setDefaultManager($sessionManager);
return $sessionManager;
},
),
),
'db' => array(
[db info here]
),
/***************************************************************************************************************
* Below is the doctrine configuration which holds information about the entities in this module and some
* other doctrine stuff like orm drivers etc.
***************************************************************************************************************/
'doctrine' => array(
'driver' => array(
'session_entities' => array(
'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/MySession/Entity')
),
'orm_default' => array(
'drivers' => array(
'MySession\Entity' => 'session_entities'
),
),
),
),
);
Module.php
<?php
namespace MySession;
/* ...required use statements... */
/***************************************************************************************************
* This class holds a few utility functions related to loading the module and accessing config
* files for the module etc. These functions are primarily used by Zend under the hood.
***************************************************************************************************/
class Module implements AutoloaderProviderInterface, ConfigProviderInterface
{
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
// create the session manager
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$sessionManager = $e->getApplication()
->getServiceManager()
->get('MySessionManager');
$sessionManager ->start();
// attach dispatch listener to validate user session
$eventManager->attach(MvcEvent::EVENT_DISPATCH, array($sessionManager, 'handleSessionValidation')); // TODO: we already handleSessionValidation on bootstrap, find out if it's necessary to do it on dispatch as well
}
/***************************************************************************************************
* Returns the location of the module.config.php file. This function is used by the Zend Framework
* underneath the hood.
***************************************************************************************************/
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
/***************************************************************************************************
* Returns the Zend StandardAutoLoader which contains the directory structure of the module source
* folder.
***************************************************************************************************/
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
MySessionManager
<?php
namespace MySession\Model;
/* ...required use statements... */
class MySessionManager extends SessionManager
{
/**
* Is this session valid?
*
* A simple validation: checks if a row for the session name exists in the database
*
* #return bool
*/
public function isValid()
{
$id = $_COOKIE[SessionVariableNames::$SESSION_NAME];
return !is_null($this->getSaveHandler()->readMetadata($id));
}
/**
* checks if the session is valid and dies if not.
*/
public function handleSessionValidation() {
if(stristr($_SERVER["SCRIPT_NAME"],"login.php"))
{
// we don't need to check the session at the login page
return;
}
if (!$this->isValid()) {
die("Not logged in.")
}
}
/**
* Start session
*
* If no session currently exists, attempt to start it. Calls
* {#link isValid()} once session_start() is called, and raises an
* exception if validation fails.
*
* #param bool $preserveStorage If set to true, current session storage will not be overwritten by the
* contents of $_SESSION.
* #return void
* #throws RuntimeException
*/
public function start($preserveStorage = false)
{
if ($this->sessionExists()) {
return;
}
$saveHandler = $this->getSaveHandler();
if ($saveHandler instanceof SaveHandlerInterface) {
// register the session handler with ext/session
$this->registerSaveHandler($saveHandler);
}
// check if old session data exists and merge it with new data if so
$oldSessionData = [];
if (isset($_SESSION)) {
$oldSessionData = $_SESSION;
}
session_start();
if ($oldSessionData instanceof \Traversable
|| (! empty($oldSessionData) && is_array($oldSessionData))
) {
$_SESSION = ArrayUtils::merge($oldSessionData, $_SESSION, true); // this may not act like you'd expect, because the sessions are stored in ArrayObjects, so the second will always overwrite the first
}
$storage = $this->getStorage();
// Since session is starting, we need to potentially repopulate our
// session storage
if ($storage instanceof SessionStorage && $_SESSION !== $storage) {
if (!$preserveStorage) {
$storage->fromArray($_SESSION);
}
$_SESSION = $storage;
} elseif ($storage instanceof StorageInitializationInterface) {
$storage->init($_SESSION);
}
$this->handleSessionValidation();
}
/**
* Write session to save handler and close
*
* Once done, the Storage object will be marked as isImmutable.
*
* #return void
*/
public function writeClose()
{
// The assumption is that we're using PHP's ext/session.
// session_write_close() will actually overwrite $_SESSION with an
// empty array on completion -- which leads to a mismatch between what
// is in the storage object and $_SESSION. To get around this, we
// temporarily reset $_SESSION to an array, and then re-link it to
// the storage object.
//
// Additionally, while you _can_ write to $_SESSION following a
// session_write_close() operation, no changes made to it will be
// flushed to the session handler. As such, we now mark the storage
// object isImmutable.
$storage = $this->getStorage();
if (!$storage->isImmutable()) {
$_SESSION = $storage->toArray(true);
$this->saveHandler->writeMetadata(null, '_metadata');
$this->saveHandler->writeData($_SESSION['_data']);
session_write_close();
$storage->fromArray($_SESSION);
$storage->markImmutable();
}
}
}
MySessionStorage
<?php
namespace MySession\Model;
/* ...required use statements... */
class MySessionStorage extends SessionArrayStorage
{
/**
* Set storage metadata
*
* Metadata is used to store information about the data being stored in the
* object. Some example use cases include:
* - Setting expiry data
* - Maintaining access counts
* - localizing session storage
* - etc.
*
* #param string $key
* #param mixed $value
* #param bool $overwriteArray Whether to overwrite or merge array values; by default, merges
* #return ArrayStorage
* #throws Exception\RuntimeException
*/
public function setMetadata($key, $value, $overwriteArray = false)
{
if ($this->isImmutable()) {
throw new Exception\RuntimeException(
sprintf('Cannot set key "%s" as storage is marked isImmutable', $key)
);
}
// set the value
$sessVar = $_SESSION['_metadata'];
if (isset($sessVar[$key]) && is_array($value)) {
// data is array, check if we're replacing the whole array or modify/add to it
if ($overwriteArray) {
$sessVar[$key] = $value;
} else {
$sessVar[$key] = array_replace_recursive($sessVar[$key], $value);
}
} else {
// data is not an array, set or remove it in the session
if ((null === $value) && isset($sessVar[$key])) {
// remove data
$array = $sessVar;
unset($array[$key]);
$_SESSION[SessionVariableNames::$SESSION_METADATA] = $array; // we can't use $sessVar here because it's only a copy of $_SESSION
unset($array);
} elseif (null !== $value) {
// add data
$sessVar[$key] = $value;
}
}
return $this;
}
/**
* Retrieve metadata for the storage object or a specific metadata key
*
* Looks at session db for the metadata
*
* Returns false if no metadata stored, or no metadata exists for the given
* key.
*
* #param null|int|string $key
* #return mixed
*/
public function getMetadata($key = null)
{
if (!isset($_SESSION)) {
return false;
}
if (null === $key) {
return $_SESSION;
}
if (!array_key_exists($key, $_SESSION)) {
return false;
}
return $_SESSION[$key];
}
/**
* Set the request access time
*
* #param float $time
* #return ArrayStorage
*/
protected function setRequestAccessTime($time)
{
// make a metadata write call, since that sets a timestamp
$this->setMetadata('datatime', new DateTime("now"));
return $this;
}
}
MySessionSaveHandler
<?php
namespace MySession\Model;
/* ...required use statements... */
/**
* This class is the back end of the $_SESSION variable, when used together with a SessionStorage and SessionManager in a ZF module
*/
class MySessionSaveHandler implements SaveHandlerInterface
{
protected $sessionService;
private $tableGateway;
private $options;
private $sessionName;
private $sessionSavePath;
private $lifetime;
public function __construct(
TableGateway $tableGateway,
DbTableGatewayOptions $options,
ISessionService $sessionService)
{
$this->tableGateway = $tableGateway;
$this->options = $options;
$this->sessionService = $sessionService;
}
protected function getSessionService()
{
return $this->sessionService;
}
/**
* Read session data
*
* #param string $id
* #return string
*/
public function read($id)
{
// Get data from database
$metadata = $this->readMetadata($id);
// Put data in PHP-session-serialized form
$data = "_metadata|".serialize($metadata);
return $data;
}
/**
* Read session metadata
*
* #param string $id
* #return mixed
*/
public function readMetadata($id = null)
{
if (is_null($id))
{
if (!array_key_exists('sessionid', $_COOKIE))
{
// can't get id from cookie
return null;
}
$id = $_COOKIE['sessionid'];
}
if ($data = $this->getSessionService()->findById($id))
{
return $data->getArrayCopy();
}
return null;
}
/** deprecated, use writeMetadata instead
* Write session data
*
* #param string $id
* #param string $data
* #return bool
* Note sessions use an alternative serialization method.
*/
public function write($id, $data)
{
// don't use this because $data is serialized strangely and can't be automatically inserted into my table
}
/**
* Write session metadata
*
* #param string $id
* #param array $data an associative array matching a row in the table
* #return mixed
*/
public function writeMetadata($id = null, $data = null)
{
if (is_null($id))
{
if (!array_key_exists('sessionid', $_COOKIE))
{
// can't get id from cookie
return null;
}
$id = $_COOKIE['sessionid'];
}
// get the session info from the database so we can modify it
$sessionService = $this->getSessionService();
$session = $sessionService->findByID($id);
if (is_null($session)) {
$session = new \MyModule\Entity\MySession();
}
if (!is_null($data))
{
// overwrite the stored data
$session->setDataFromArray($data);
}
return $sessionService->save($session);
}
/**
* Destroy session - deletes data from session table
*
* #param string $id The session ID being destroyed.
* #return bool
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
*/
public function destroy($id)
{
$this->getSessionService()->delete($id);
return true;
}
/**
* Garbage Collection - cleanup old sessions
*
* #param int $maxlifetime
* Sessions that have not updated for
* the last maxlifetime seconds will be removed.
* #return bool
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
*/
public function gc($maxlifetime)
{
$metadata = $this->readMetadata(); // gets session id from cookie, then gets session from that
if (!is_null($metadata))
{
$datatime = $metadata['datatime'];
$previousTime = (new DateTime($datatime))->getTimestamp();
// if (current time - datatime) > maxlifetime, destroy the session
$val = time() - $previousTime;
if ($val > $maxlifetime) {
$this->destroy($metadata['sessionid']);
}
}
}
}
The end result of all this is that you can access information stored in the database simply by accessing the $_SESSION variable, because the data gets loaded from the database into the $_SESSION variable on bootstrap, and the $_SESSION variable is written back into the database when the session is closed (which as I understand it, happens when the page is sent to the client).

CodeIgniter creating new captcha images at refresh

Goodday Stackoverflow,
I've implented the Captcha-helper in my project successfully. The image is being displayed, saved and what not, although the image itself is created again and again and again after every refresh. After 10 refreshes, my captcha folder contains 10 images.
Is this normal?
This is my code;
...
/**
* Method that returns the currently
* active Captcha code of the user.
*
* #access public
* #return string - user's captcha code
* #since v0.1.0.0
*/
public function get_captcha()
{
// Check for already existing captcha's.
if ($this->is_captcha())
{
// Get the captcha.
$data = $this->get_captcha_data();
}
else
{
// Create a new captcha.
if ($this->insert_captcha())
$data = $this->get_captcha_data();
}
// Set the values.
$captcha_vals = array(
'word' => $data['word'],
'img_path' => './media/sm_general/captcha/',
'img_url' => base_url().'media/sm_general/captcha/',
'img_width' => '175',
'img_height' => '60'
);
// Return the captcha.
return create_captcha($captcha_vals);
}
/**
* Method that returns the captcha data
* of a specific user.
*
* #access private
* #param string $field - (optional) specific to be returned data
* #return array/string - data of the captcha
* #since v0.1.0.0
*/
private function get_captcha_data($field='')
{
// Prepare the query.
$this->db->select('*');
$this->db->from('sm_sessions_captcha');
$this->db->where('ip_address',$this->input->ip_address());
$this->db->limit(1);
// Execute the query.
$query = $this->db->get();
// Get the result.
$result = $query->row_array();
// Return the data.
if ( ! empty($field))
return $result[$field];
else
return $result;
}
/**
* Method that returns a random word from
* the database.
*
* #access private
* #return string - randomly selected word
* #since v0.1.0.0
*/
private function get_word_random()
{
// Prepare the query.
$this->db->select('word');
$this->db->from('sm_sessions_captcha_words');
$this->db->order_by('word','RANDOM');
$this->db->limit(1);
// Execute the query.
$query = $this->db->get();
// Get the result.
$result = $query->row_array();
// Return the word.
return $result['word'];
}
/**
* Method that creates a new captcha image.
*
* #access private
* #return array
* #since v0.1.0.0
*/
private function insert_captcha()
{
// Prepare the query.
$data = array(
'captcha_time' => (time() + 7200),
'ip_address' => $this->input->ip_address(),
'word' => $this->get_word_random()
);
// Execute the query.
if ($this->db->insert('sm_sessions_captcha',$data))
return true;
else
return false;
}
/**
* Check if a captcha already exists.
*
* #access private
* #return boolean - exists or not
* #since v0.1.0.0
*/
private function is_captcha()
{
// Set the expiration time.
$expiration = time() - 7200;
// Remove all expired captchas.
$this->db->where('captcha_time <',$expiration);
$this->db->delete('sm_sessions_captcha');
// Prepare the query.
$this->db->select('*');
$this->db->from('sm_sessions_captcha');
$this->db->where('captcha_time >=',$expiration);
$this->db->where('ip_address',$this->input->ip_address());
$this->db->limit(1);
// Execute the query.
$query = $this->db->get();
// Return the existence.
if ($query->num_rows() == 1)
return true;
else
return false;
}
...
I have no idea if this is working as intended. Please enlighten me if I am mistaking.
Cheers.
This is normal, every refresh a new captcha image is generated, then CI runs a garbage collection, so you can set how much time the old captcha image should remain inside the folder by "expiration" value
http://ellislab.com/codeigniter/user-guide/helpers/captcha_helper.html
$cap = create_captcha($vals);
$this->session->set_userdata('time', $cap['time']);
Write in when data store in database.
Above that:
unlink(realpath('captcha').'/'.$this->session->userdata('time').'.jpg');

How do I set the session cookie path in cakePHP while still saving session data in the database?

By default cakePHP puts the session cookie at the application level. For instance, if you have a cakePHP application at www.mydomain.com/myapp the cookie is stored at "/myapp".
I need my session data available to other applications at www.mydomain.com, so I would like to have the session cookie stored at "/" instead.
I found a bunch of articles on how to do this, but when using the methods explained in both of these popular posts/guides, it disables sessions being saved in the database.
How to bend CakePHP's session handling to your needs
cakePHP - Cookbook - Sessions
Does anyone know how to change the session cookie path to "/" while still being able to save session data in the database?
Thanks!
Can this not be done at the PHP level rather than in CakePHP itself?
So you can use session_set_save_handler() (man page) in the following sort of way to achieve sessions stored in the database. The following code is from a comment on the PHP manual page by maria at junkies dot jp and I have not specifically tested it:
<?php
class Session
{
/**
* a database connection resource
* #var resource
*/
private $_sess_db;
/**
* Open the session
* #return bool
*/
public function open() {
if ($this->_sess_db = mysql_connect(SESSION_DB_HOST,
SESSION_DB_USER,
SESSION_DB_PASS)) {
return mysql_select_db(SESSION_DB_DATABASE, $this->_sess_db);
}
return false;
}
/**
* Close the session
* #return bool
*/
public function close() {
return mysql_close($this->_sess_db);
}
/**
* Close the session
* #return bool
*/
public function close() {
return mysql_close($this->_sess_db);
}
/**
* Read the session
* #param int session id
* #return string string of the sessoin
*/
public function read($id) {
$id = mysql_real_escape_string($id);
$sql = sprintf("SELECT `data` FROM `sessions` " .
"WHERE id = '%s'", $id);
if ($result = mysql_query($sql, $this->_sess_db)) {
if (mysql_num_rows($result)) {
$record = mysql_fetch_assoc($result);
return $record['data'];
}
}
return '';
}
/**
* Write the session
* #param int session id
* #param string data of the session
*/
public function write($id, $data) {
$sql = sprintf("REPLACE INTO `sessions` VALUES('%s', '%s', '%s')",
mysql_real_escape_string($id),
mysql_real_escape_string($data),
mysql_real_escape_string(time()));
return mysql_query($sql, $this->_sess_db);
}
/**
* Destoroy the session
* #param int session id
* #return bool
*/
public function destroy($id) {
$sql = sprintf("DELETE FROM `sessions` WHERE `id` = '%s'", $id);
return mysql_query($sql, $this->_sess_db);
}
/**
* Garbage Collector
* #param int life time (sec.)
* #return bool
* #see session.gc_divisor 100
* #see session.gc_maxlifetime 1440
* #see session.gc_probability 1
* #usage execution rate 1/100
* (session.gc_probability/session.gc_divisor)
*/
public function gc($max) {
$sql = sprintf("DELETE FROM `sessions` WHERE `timestamp` < '%s'",
mysql_real_escape_string(time() - $max));
return mysql_query($sql, $this->_sess_db);
}
}
//ini_set('session.gc_probability', 50);
ini_set('session.save_handler', 'user');
$session = new Session();
session_set_save_handler(array($session, 'open'),
array($session, 'close'),
array($session, 'read'),
array($session, 'write'),
array($session, 'destroy'),
array($session, 'gc'));
// below sample main
session_start();
session_regenerate_id(true);
?>
So you could reference that in your bootstrap file.
Basically, sessions in a normal server should be global by default. At least have been in all servers, that I have used.. So I would actually change my server before some hardcore work-around in the script.
However, if you are using some session related class, that has __destruct() function at the end..and in it, you have session_destroy(); OR it doesn't really matter, where the session_destroy(); sits, but it does matter for your path related issue. I ran some quick tests and sessions by default are global within the domain...however, on the second page obviously the variable isn't being displayed, if the trigger page kills it.
My test:
trigger.php file:
<?
session_start();
echo $_SESSION['foo'] = 'bar';
// session_destroy(); <- This kills it
// Otherwise $_SESSION['foo'] gets displayed on the child.php file in test/ directory
?>
test/child.php file:
<?
session_start();
echo $_SESSION['foo'];
?>

Categories