Store multiple laravel session in once - php

Hello i'd like to store some laravel sessions in once
so instead of doing like this :
// Storing Sessions
$request->session()->put('term', $request->term);
$request->session()->put('section', $request->section);
// Accessing Sessions
$request->session()->get('term');
$request->session()->get('section');
I want something like :
// Storing Sessions
$sessions = [
'term' => $request->term,
'section' => $request->section,
];
$request->session()->put($sessions);
// Accessing Sessions
$request->session()->get('term');
$request->session()->get('section');
But couldn't find the way.
I tried something like this , but the code for accesssing is ugly and too long:
$request->session()->put('my-sessions', [
'term' => $request->term,
'section' => $request->section,
]);
// Too long
$request->session()->get('my-sessions')['term'];
$request->session()->get('my-sessions')['section'];
I know foreach will do the job, but Is there more a "Laravel way" or built in function to do that ?

Related

incrementing a field in firestore (php)

I am looking for a way to increment a field in firestore like in this documentation.
shard_ref.update("count", firebase.firestore.FieldValue.increment(1));
The only caveat is that I am trying to use firestore Client Sdk or the Admin Sdk for php.
What is a clean solution for incrementing a field in php. Does it require both read and write operations or is it possible with only write operation?
Firestore Client Sdk has the ability to increment a field. (use negative values to decrement).
You can use it in the following way to update the field with $field_name:
$data = [
[
'path' => $field_name,
'value' => \Google\Cloud\Firestore\FieldValue::increment($increment_value);
]
];
$doc_ref->update($data);
If you are using a batching you call it as follows
$data[$field_name] = \Google\Cloud\Firestore\FieldValue::increment($increment_value);
$batch->add_to_batch('update', $doc_ref, $data);
Here is additional reference
if you have more data you can do
$data = [
'name' => $name,
'email' => $email,
'likes'=> \Google\Cloud\Firestore\FieldValue::increment(1) // Add + 1
] ;

Store slug and path based on post ID which is created before in Laravel

I have a quite complicated form, which has a lot of fields - everything works fine, but I also need to store a slug and path to thumbnail. The problem is, that when I am using store method and create a post, I need to dynamically create that slug and path based on the post ID (which is not created yet and therefore I don't have an ID).
This is the chunk of the code I use to store the main post data:
Post::forceCreate([
'title' => $title,
'slug' => create_url_slug($title, $id),
'thumbnail' => thumbPath($id),
'description' => request('description'),
'password' => bcrypt(request('password')),
'user_id' => get_user_id()
]);
Here I passed two functions -> create_url_slug and thumbPath. If I put these functions above this chunk of code, the error will be thrown because the ID does not exist yet. On the other hand, if I put these functions under this code, the error will also appear, because those functions would be undefined. Can I somehow use closures or divide this method to two parts?
Thanks anybody in advance.
A way to do this is to create the model and right after assigning the values for example
$post = Post::forceCreate([
'title' => $title,
'description' => request('description'),
'password' => bcrypt(request('password')),
'user_id' => get_user_id()
]);
$post->slug = create_url_slug($title, $post->id);
$post->thumbnail = thumbPath($post->id);
$post->save();
I do not recall if make creates an id for the Model else you could use Post::make and save a call to the database. Worth a try.

ZF3 Session empty on each page load

In ZF3, I'm initializing my session as following:
config/autoload/global.php
'session_config' => [
'name' => 'gintra3',
'cookie_lifetime' => 60*60*1,
'gc_maxlifetime' => 60*60*24*30,
],
'session_storage' => [
'type' => Zend\Session\Storage\ArrayStorage::class,
],
'session_manager' => [
'storage' => Zend\Session\Storage\SessionArrayStorage::class,
'validators' => [
Zend\Session\Validator\RemoteAddr::class,
Zend\Session\Validator\HttpUserAgent::class,
],
],
module/Application/Module.php
$sessionManager = $e->getApplication()->getServiceManager()->get(SessionManager::class);
$sessionTableGateway = new TableGateway('session',$e->getApplication()->getServiceManager()->get('Zend\Db\Adapter\Adapter'));
$sessionSaveHandler = new DbTableGateway($sessionTableGateway,new DbTableGatewayOptions());
$sessionManager->setSaveHandler($sessionSaveHandler);
Now, when I'm storing data like this on page 1:
$sessionC = new Container('test');
$sessionC->testVariable = "helloWorld";
And retrieve it on page 2 like this:
$sessionC = new Container('test');
Debug::dump($sessionC->testVariable);
I get an output on page 2 like this:
vendor/zendframework/zend-debug/src/Debug.php:97:null
When I check the database table, the 'data' column of the corresponding session_id contains "helloWorld" after loading page 1. After loading page 2, it does not contain "helloWorld" anymore.
If I comment out all session-related lines in Module.php, a PHPSESSID cookie is created on page load and session data saving and storing works fine now.
It does not matter if I use DbTableGateway as a save_handler or just use standard save_handler. The problem occurs in both cases.
When using my individual session storage, also FlashMessenger and Csrf-Form-Validation stop working due to the same mechanism of loosing all session data on page load.
Where is my problem? What am I overseeing?
Thanks a lot for some ideas.

CakePHP HABTM Save Single Header Record, linked against multiple existing children

I am using CakePHP 2.9.1, have 2 tables in a HABTM relationship, I want to save the header record and save many associations in one go, but the child record is already existing, I seem to be able to save the data using a hardcoded list but not using a variable, I'm sure it's something silly I'm doing, I'm running inside a plugin, the model I want to link too is in the main code.
The child record must not be altered because it's handled by the main system, I'm just extending it's functionality by linking to it in our plugin.
// Header Record
class templatedoc extends TemplateModuleAppModel
{
public $useTable = 'templatedocs';
public $hasAndBelongsToMany = [
'Servicetemplate' => [
'className' => 'Servicetemplate',
'joinTable' => 'templatedocs_to_servicetemplates',
'foreignKey' => 'templatedoc_id',
'associationForeignKey' => 'servicetemplate_id',
'unique' => true,
'dependent' => false, // We don't want to delete the Service Template by mistake!
]
];
}
Here is my save, this works:
$this->templatedoc->create ();
$data = [
'templatedoc' => [
'warning_adjust' => $prioritywarn,
'summary' => $summary,
],
'Servicetemplate' => [
1,2,3,10 // Dynamic data needed here!
]
];
$result = $this->templatedoc->SaveAll ($data);
But I can't have the "Servicetemplate" hardcoded, I've tried passing an array inside that array and removing it, imploding an array to make a comma separated list all I end up with is the header 'templatedoc' record being created but nothing in my link table
Only thing I can think is a difference is that the hardcoded list are int values, by data is coming from the database so will be an int inside a string, but I can't see that being an issue.
Any ideas what I'm doing wrong? Sorry if it's something completely stupid.
What I had to do in the end was store the items forcing an int, when associating in my foreach loop.
$items[] = (int) $id
Then this worked:
$this->templatedoc->create ();
$data = [
'templatedoc' => [
'warning_adjust' => $prioritywarn,
'summary' => $summary,
],
'Servicetemplate' => [
$items // Dynamic data needed here!
]
];
$result = $this->templatedoc->SaveAll ($data);
Seems very fussy it has to be type int, adding here in case someone else gets caught out, would love to know if there is a better solution.

Silex: Get Authenticated User Information on Routes Outside of Firewall

I am using Silex 2.0 (I know - it's development version and not fully released yet) along with CNAM's JWT security provider (see: https://github.com/cnam/security-jwt-service-provider) to write an API for an open source application I am writing.
In short, there are three types of users that I care about:
Sitewide admins (ROLE_ADMIN) that have complete access
Commissioners (ROLE_COMMISH) who create objects they own, and can edit their own objects
Anonymous users who access read-only information.
As such, there are three sections of routes that go along with these "roles":
/admin/* where administrators can perform their uber actions
/commish/* where commissioners or admins can perform their actions on their objects
/* where all users can read information
The issue that I've come across is that while I can setup 3 firewalls, one for each, there are times in the 3rd route category (GET /object/1 for instance) where it needs to be accessibly anonymously, but if the user provides a valid JWT token, I need to access that user in order to perform some additional logic on the data I hand back in the response.
As I have it setup currently (more on my config below), it's all-or-nothing: I either restrict an entire firewall to only authenticated users with a certain role, or I open it up to anonymous users (and therefore cannot view user information).
Is it possible to have a route that anyone can hit, but logged in users can also be seen?
Current security configuration:
$app['users'] = function () use ($app) {
return new UserProvider($app);
};
$app['security.jwt'] = [
'secret_key' => AUTH_KEY,
'life_time' => 86400,
'algorithm' => ['HS256'],
'options' => [
'header_name' => 'X-Access-Token'
]
];
$app['security.firewalls'] = array(
'login' => [
'pattern' => 'login|register|verify|lostPassword|resetPassword',
'anonymous' => true,
],
'admin' => array(
'pattern' => '^/admin',
'logout' => array('logout_path' => '/logout'),
'users' => $app['users'],
'jwt' => array(
'use_forward' => true,
'require_previous_session' => false,
'stateless' => true,
)
),
'commish' => array(
'pattern' => '^/commish',
'logout' => array('logout_path' => '/logout'),
'users' => $app['users'],
'jwt' => array(
'use_forward' => true,
'require_previous_session' => false,
'stateless' => true,
)
)
);
$app['security.role_hierarchy'] = array(
'ROLE_ADMIN' => array('ROLE_MANAGER'),
);
$app->register(new Silex\Provider\SecurityServiceProvider());
$app->register(new Silex\Provider\SecurityJWTServiceProvider());
Additionally, I've attempted another approach where I match all routes under a single firewall, but then protect certain ones by using securty.access_rules configuration, but it does not work. An example of what I've tried:
$app['security.firewalls'] = array(
'api' => array(
'pattern' => '^/',
'logout' => array('logout_path' => '/logout'),
'anonymous' => true,
'jwt' => array(
'use_forward' => true,
'require_previous_session' => false,
'stateless' => true
)
)
);
$app['security.access_rules'] = array(
array('^/admin', 'ROLE_ADMIN'),
array('^/commish', 'ROLE_MANAGER'),
array('^/', 'IS_AUTHENTICATED_ANONYMOUSLY')
);
You can use $app['security.jwt.encoder'] to decode jwt and either create a custom trait and extending the route object or using midddlewareeeither on the route level or an easier way would be to use a middleware on the application level. I had similar issue and this is how i solved it, something like below
ex.
$app->before(function (Request $request, Application $app) {
$request->decodedJWT = $app['security.jwt.encoder']->
decode($request->headers->get('X-Access-Token'));
});
and then you can access the decoded jwt form any route by doing this
$app->get('/object/1', function(Request $request) {
$decodedJWT = $request->decodedJWT;
// do whatever logic you need here
})
So: so far I have not found this to be possible through the "normal" way, which is disappointing. I will not mark what I detail below as the "answer" for a few days, hoping that someone can chime in and offer a better, more "official" way to solve the dilemma.
TL;DR: I manually check the request headers for the access token string, then decode the token using the JWT classes in order to load the user account in routes outside of the firewall. It's incredibly hacky, it feels downright dirty, but it's the only solution to the issue that I see at the moment.
Technical Details: First, you must acquire the token value from the request header. Your controller method will have been handed a Symfony\Component\HttpFoundation\Request object, from which you can access $request->headers->get('X-Access-Token'). In most instances the user will not be authenticated, so this will be empty, and you can return null.
If not empty, you must then use Silex's instance of JWTEncoder to decode the token contents, create a new token instance of JWTToken, set the context to the decoded value from the encoder, and finally you can access the username property from said token - which can then be used to grab the corresponding user record. An example of what I came up with:
$request_token = $request->headers->get('X-Access-Token','');
if(empty($request_token)) {
return null;
}
try {
$decoded = $app['security.jwt.encoder']->decode($request_token);
$token = new \Silex\Component\Security\Http\Token\JWTToken();
$token->setTokenContext($decoded);
$userName = $token->getTokenContext()->name;
//Here, you'd use whatever "load by username" function you have at your disposal
}catch(\Exception $ex) {
return null;
}
And obviously, any code calling this function would need to know that because the request is outside of the firewall, there is zero guarantee that a user will be returned (hence the hacky try-catch that silences exceptions by just returning null).
Edit: I've updated the code here to use Silex's built-in DI container (provided by Pimple) so there's no need to create a new instance of the JWT encoder by hand. I'm also marking #user5117342 's answer as the correct one, as using some sort of Silex middleware approach is far more robust.
Edit (April 2016): Using the updated cnam/security-jwt-service 2.1.0 along with symfony/security 2.8, there's a slight update that makes the code above a little simpler:
$request_token = $request->headers->get('X-Access-Token','');
if(empty($request_token)) {
return null;
}
try {
$decodedToken = $app['security.jwt.encoder']->decode($request_token);
$userName = $decodedToken->name;
//Here, you'd use whatever "load by username" function you have at your disposal
}catch(\Exception $ex) {
return null;
}
The issue with the newer dependencies is that the JWTToken constructor requires 3 parameters which are difficult to obtain in most service layers, not to mention is quite out of place. As I was updating my Composer dependencies, I ended up finding out that I didn't actually need to create a JWTToken in order to get the username I needed.
Of course, it's to be noted I'm only using this method on public (anonymous) API routes to provide some niceties to users who are logged in - my app doesn't deal with sensitive data so I'm not overly concerned with this avenue outside of the firewalls. At worst a black hat user would end up seeing non-sensitive data that they normally wouldn't, but that's it. So YMMV.
Your are must be use regular expression e.g.
$app['security.firewalls'] = array(
'login' => [
'pattern' => 'login|register|oauth',
'anonymous' => true,
],
'secured' => array(
'pattern' => '^/api|/admin|/manager',
'logout' => array('logout_path' => '/logout'),
'users' => $app['users'],
'jwt' => array(
'use_forward' => true,
'require_previous_session' => false,
'stateless' => true,
)
),
);

Categories