ZF3 Problem adding trusted proxies to session validator - zend-framework3

I am hosting a Zend Framework 3 site on MS Azure. There is a problem with the session validation as Azure's Application Gateway is acting as a reverse proxy which the remote address validator doesn't like so the session is not read.
I can see from the class reference for remote address that there is a method setTrustedProxies()
which, according to the documenation, I can pass an array of IP addresses to. However I don't really have much of an idea of how to do this.
I am configuring the session_manager in global.php
'session_manager' => [
// Session validators (used for security).
'validators' => [
RemoteAddr::class,
HttpUserAgent::class,
],
],
Then, in Module.php I am instantiating the session manager using
$sessionManager = $serviceManager->get(SessionManager::class);
I am then trying to add the trusted proxies using the following with fake IPs
$sessionManager = $serviceManager->get(SessionManager::class);
$request = $serviceManager->get('Request');
$remAdd = $request->getServer()->get('REMOTE_ADDR');
$remoteAddr = new RemoteAdddress($remAdd);
$remoteAddr->setTrustedProxies(['192.98.98.11', '187.2.2.10']);
$remoteAddr->setProxyHeader('X-Forwarded-For');
$remoteAddr->setUseProxy($useProxy = true);
$chain = $sessionManager->getValidatorChain();
$chain->attach('session.validate', array($remoteAddr, 'isValid'));
I am almost certain this is not the correct way to do this but I can't find any documentation online about setting the trusted proxies.
If I do
$chain = $sessionManager->getValidatorChain();
print_r($chain);
after adding the proxies I don't see any reference to proxies in the output
Zend\Session\ValidatorChain Object
(
[events:protected] => Array
(
[session.validate] => Array
(
[1] => Array
(
[0] => Array
(
[0] => Array
(
[0] => Zend\Session\Validator\RemoteAddr Object
(
[data:protected] => 127.0.0.1
)
[1] => isValid
)
)
)
)
)
[eventPrototype:protected] => Zend\EventManager\Event Object
(
[name:protected] =>
[target:protected] =>
[params:protected] => Array
(
)
[stopPropagation:protected] =>
)
[identifiers:protected] => Array
(
)
[sharedManager:protected] =>
[storage:protected] => Zend\Session\Storage\SessionArrayStorage Object
(
)
)
As I said, I am pretty sure that I am going about this the wrong way so would be very grateful for any help in finding the correct method.

I have stumbled upon the exact same issue, only in my case I am using CF.
Your approach is wrong because you are using Zend\Http\PhpEnvironment\RemoteAddress and you are not replacing the already loaded validator Zend\Session\Validator\RemoteAddr.
Zend\Session\Validator\RemoteAddr is internally using Zend\Http\PhpEnvironment\RemoteAddress so you should not use Zend\Http\PhpEnvironment\RemoteAddress: https://github.com/zendframework/zend-session/blob/master/src/Validator/RemoteAddr.php
Here is the working code, tested:
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$serviceManager = $application->getServiceManager();
// The following line instantiates the SessionManager and automatically
// makes the SessionManager the 'default' one to avoid passing the
// session manager as a dependency to other models.
$sessionManager = $serviceManager->get(SessionManager::class);
$remoteAddr = new \Zend\Session\Validator\RemoteAddr();
//$remoteAddr->setTrustedProxies([]);
$remoteAddr->setProxyHeader('CF-Connecting-IP');
$remoteAddr->setUseProxy(true);
$current_chain = $sessionManager->getValidatorChain(\Zend\Session\Validator\RemoteAddr::class);
$current_chain->attach(
'session.validate',
[ new \Zend\Session\Validator\HttpUserAgent(), 'isValid' ]
);
$current_chain->attach(
'session.validate',
[ $remoteAddr, 'isValid' ]
);
$sessionManager->start();
Container::setDefaultManager($sessionManager);
}
Result:
Zend\Session\ValidatorChain Object
(
[events:protected] => Array
(
[session.validate] => Array
(
[1] => Array
(
[0] => Array
(
[0] => Array
(
[0] => Zend\Session\Validator\HttpUserAgent Object
(
[data:protected] => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36
)
[1] => isValid
)
[1] => Array
(
[0] => Zend\Session\Validator\RemoteAddr Object
(
[data:protected] => MY_REAL_IP
)
[1] => isValid
)
)
)
)
)
)
I also added print_r($remoteAddress); inside https://github.com/zendframework/zend-session/blob/master/src/Validator/RemoteAddr.php function and test with TrustedProxies:
/**
* Returns client IP address.
*
* #return string IP address.
*/
protected function getIpAddress()
{
$remoteAddress = new RemoteAddress();
$remoteAddress->setUseProxy(static::$useProxy);
$remoteAddress->setTrustedProxies(static::$trustedProxies);
$remoteAddress->setProxyHeader(static::$proxyHeader);
print_r($remoteAddress);
return $remoteAddress->getIpAddress();
}
Result:
Zend\Http\PhpEnvironment\RemoteAddress Object
(
[useProxy:protected] => 1
[trustedProxies:protected] => Array
(
[0] => 192.98.98.11
[1] => 187.2.2.10
)
[proxyHeader:protected] => HTTP_CF_CONNECTING_IP
)
The conclusion is that setTrustedProxies is working, you just cannot see it because https://github.com/zendframework/zend-session/blob/master/src/Validator/RemoteAddr.php does not return this for it to be visible since it just passes it to Zend\Http\PhpEnvironment\RemoteAddress.
You can of course overwrite the 'default' SessionManager exactly like your code:
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config);
$sessionManager = new SessionManager($sessionConfig);
But this is just optional.

Related

Return Doctrine_Null Object when insert record in Symfony

I am new to Symfony, i am facing an issue when i tried to insert a record. Here is part of my code at FormRequestDao.php:
public function saveFormRequest(FormRequest $formRequest, $formList, $entitlements) {
$conn = Doctrine_Manager::connection();
$conn->beginTransaction();
print_r($formRequest);
$formRequest->save();
...
return $formRequest;
}
I always get the value "note" in null/blank, it just a normal textbox.
Here is the partial result of print_r($formRequest):
[_data:protected] => Array
(
[id] => Doctrine_Null Object
(
)
[product_type_id] => 1
[date_inserted] => 2017-05-31
[item_number] => 0002
[description] => This is a product 1.
[note] => Doctrine_Null Object
(
)
)
And the value of "note" has been captured in productApplicationService.php:
public function insertProduct(ProductParameterObject $productAssignmentData) {
print_r($productAssignmentData);
return $this->saveFormRequest($productAssignmentData);
}
Any clue for me?
Thanks.
If you need a response for your twig file you need to return this for example
return $this->render(':yourtwig.twig', [
'yourEntity' => $this->saveFormRequest($productAssignmentData)
]);

How to access the fields from the form data in symfony 3

I want to use how to access the username and password fields from the below code.
AppBundle\Entity\MasterUserAccount Object
(
[id:AppBundle\Entity\MasterUserAccount:private] =>
[firstName:AppBundle\Entity\MasterUserAccount:private] =>
[lastName:AppBundle\Entity\MasterUserAccount:private] =>
[username:AppBundle\Entity\MasterUserAccount:private] => test
[password:AppBundle\Entity\MasterUserAccount:private] => 123456
[createdBy:AppBundle\Entity\MasterUserAccount:private] =>
[updatedBy:AppBundle\Entity\MasterUserAccount:private] =>
[createdAt:AppBundle\Entity\MasterUserAccount:private] =>
[updatedAt:AppBundle\Entity\MasterUserAccount:private] =>
[status:AppBundle\Entity\MasterUserAccount:private] =>
)
$data = $form->getData();
$username = $data['username'];
I'm getting the below error the following error
Cannot use object of type AppBundle\Entity\classname as array
The data that you get out of a symfony form is an Entity. You can't access an attribute of this entity, as you do for arrays.
Instead you should be using getter method defined inside class MasterUserAccount.
So. It should be:
$username = $data -> getUserName();
This is assuming that getter method is already defined inside MasterUserAccount class. If not, create a getter method first.

Doctrine / Symfony: convert custom type to database value before using QueryBuilder

I have defined a custom Doctrine data type for Uuid. When I search for an object using find($uuid), it works correctly, i.e. the attribute is converted using convertToDatabaseValue() before executing the query, and converted back with convertToPhpValue() when value is retrieved.
The conversion doesn't work if I use the QueryBuilder. Example:
$qb = $this->createQueryBuilder('s');
$qb = $qb->where( //some conditions...
$qb->expr()->eq( 's.uuid', ':uuid' ))->setParameter( 'uuid', $uuid );
I found two similar unanswered questions:
Symfony Doctrine datatype only works in findBy not querybuilder
Doctrine 2 Custom Types
It looks like that the conversion is in fact ignored.
How can I force the conversion of the parameter before executing the query? Is there a way to access the convertToDatabaseValue() function of the custom data type from the repository?
Thanks
Yes setParameter() has third parameter, but the type of third param as string is worked for me not the object.
You can do it in following way.
$qb = $this->createQueryBuilder('s');
$qb = $qb->where( //some conditions...
$qb->expr()->eq( 's.uuid', ':uuid' ))->setParameter( 'uuid', $uuid, 'uuid' );
If you dont know what exactly key is for datatype 'uuid' is.
Then use print_r(Type::getTypesMap()); to get list of all dataypes added.
In my case it was
Array
(
[array] => Doctrine\DBAL\Types\ArrayType
[simple_array] => Doctrine\DBAL\Types\SimpleArrayType
[json_array] => Doctrine\DBAL\Types\JsonArrayType
[object] => Doctrine\DBAL\Types\ObjectType
[boolean] => Doctrine\DBAL\Types\BooleanType
[integer] => Doctrine\DBAL\Types\IntegerType
[smallint] => Doctrine\DBAL\Types\SmallIntType
[bigint] => Doctrine\DBAL\Types\BigIntType
[string] => Doctrine\DBAL\Types\StringType
[text] => Doctrine\DBAL\Types\TextType
[datetime] => Doctrine\DBAL\Types\DateTimeType
[datetimetz] => Doctrine\DBAL\Types\DateTimeTzType
[date] => Doctrine\DBAL\Types\DateType
[time] => Doctrine\DBAL\Types\TimeType
[decimal] => Doctrine\DBAL\Types\DecimalType
[float] => Doctrine\DBAL\Types\FloatType
[binary] => Doctrine\DBAL\Types\BinaryType
[blob] => Doctrine\DBAL\Types\BlobType
[guid] => Doctrine\DBAL\Types\GuidType
[geometry] => CrEOF\Spatial\DBAL\Types\GeometryType
[point] => CrEOF\Spatial\DBAL\Types\Geometry\PointType
[polygon] => CrEOF\Spatial\DBAL\Types\Geometry\PolygonType
[linestring] => CrEOF\Spatial\DBAL\Types\Geometry\LineStringType
)
And my doctrine code was something like this.
$queryBuilder = $this->createQueryBuilder('c');
$queryBuilder
->where('st_contains(:polygon, point(c.latitude, c.longitude) ) = 1')
->setParameter('polygon', $city->getPolygon(), 'polygon');
Here's the solution: the function setParameter() has a third argument $type which is used to declare the typology of the parameter. The custom declared type can be retrieved with the getType() function of the Doctrine Type class:
$qb = $this->createQueryBuilder('s');
$qb = $qb->where( //some conditions...
$qb->expr()->eq( 's.uuid', ':uuid' ))->setParameter( 'uuid', $uuid, Type::getType('uuid') );

Using token as data selector

I created the following token; however, when I try to use site:coupons as a data selector in a loop action
It does not appear in data selection browser. Note that it does appear as replacement pattern when i use for example "Show a message on the site" action.
I spent lot of time searching in the internet and rules' token' issue queue, i tried to read the source codes of core token , token and rules as well. I also found some information too like data selector are no tokens! or rules only works with entities!
So far i couldn't get this to work no matter hard i tried. My data is not entity. Is there anyway to integrate it with rules?
I couldn't find any official documentation on this so i created an issue with hope that some of the rule's experts can help me out.
Note : if i replace site with coupon-link in the following code, it won't even appear as replacement pattern in rules. but it works fine as token anywhere else but in rules
Thanks in advance
<?php
/**
* Implements hook_token_info().
*/
function coupon_link_token_info() {
$types['coupon-link'] = array(
'name' => t("Coupon link coupon info"),
'description' => t("Info about linked coupon via url."),
);
// Andy Pangus specific tokens.
$tokens['site']['coupon-code'] = array(
'name' => t("Coupon Link Coupon Code"),
'description' => t("The code of the coupon entered via url."),
);
$tokens['site']['coupon'] = array(
'name' => t("Coupon Link Coupon"),
'description' => t("The coupon entered via url."),
'type' => 'commerce_coupon'
);
$tokens['site']['coupons'] = array(
'name' => t("Coupon Link List Coupons"),
'description' => t("The coupons entered via url."),
'type' => 'array'
);
return array(
'types' => $types,
'tokens' => $tokens,
);
}
/**
* Implements hook_tokens().
*
* #ingroup token_example
*/
function coupon_link_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
$sanitize = !empty($options['sanitize']);
// Text format tokens.
if ($type == 'site' && __coupon_link_get_coupon_code()) {
//$format = $data['format'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'coupon-code':
// Since {filter_format}.format is an integer and not user-entered
// text, it does not need to ever be sanitized.
$replacements[$original] = $sanitize ? filter_xss(__coupon_link_get_coupon_code()) : __coupon_link_get_coupon_code();
break;
case 'coupon':
// Since the format name is user-entered text, santize when requested.
$replacements[$original] = __coupon_link_get_coupon(__coupon_link_get_coupon_code());
break;
case 'coupons':
// Since the format name is user-entered text, santize when requested.
$replacements[$original] = array(__coupon_link_get_coupon(__coupon_link_get_coupon_code()));
break;
}
}
}
return $replacements;
}
?>
A few things.
Tokens are formatted as [type:token] as explained on the hook_token_info api page. For your example, it would be [coupon-link:coupon]. I'm not sure why you're appending your tokens to the site array, as your custom coupon token probably has nothing to do with sitewide tokens like *site_url* or *site_name*.
Because the types are machine names, you should change it to coupon_link as machine names with dashes are not Drupal standard.
If you truly get lost, I suggest also looking at the token example from the examples module.

FQL: query for event table returns venue.name instead of venue.id

When making a query to get event details I seem to get venue.name instead of venue.id in the result set. Has there been an unannounced change in the table structure or am I doing something wrong. The Graph API Explorer gives the venue.id yet when using FQL through PHP SDK in my own web site it's the venue.name I get.
Heres the code:
$fql='{
"event_info": "SELECT name,description, pic_small,pic_big, eid,venue,location FROM event WHERE eid ='.$_GET['id'].'",
"event_venue":"SELECT name, username, page_id, location FROM page WHERE name IN (SELECT venue.id FROM #event_info)"
}';
$setup = array(
'method' => 'fql.multiquery',
'queries' => $fql,
'callback' => ''
);
$result = $facebook->api($setup);
This leads to the "event_venue" result set to be empty.
Here's the dump:
Array
(
[0] => Array
(
[name] => event_info
[fql_result_set] => Array
(
[0] => Array
(
[eid] => 410351692336116
[venue] => Array
(
[name] => Boothill
)
[location] => Boothill
)
)
)
[1] => Array
(
[name] => event_venue
[fql_result_set] => Array
(
)
)
)
If I test this query
SELECT name,description, pic_small,pic_big, eid,venue,location
FROM event WHERE eid ='410351692336116'
using the FQL tab (!) in the Graph API explorer, I get
"venue": {
"id": 126049334121592
}
and not the venue’s name.
And doing a multiquery like your’s with the second query being
"event_venue":"SELECT name, username, page_id, location FROM page
WHERE page_id IN (SELECT venue.id FROM #event_info)"
I’m getting the venue info as well.
Could you please check if you get a different result if you do your queries not using your $setup array with
'method' => 'fql.multiquery',
but just
$facebook->api('/fql?q='.urlencode('{ "event_info": "…", "event_venue": "… FROM page
WHERE page_id IN (SELECT venue.id FROM #event_info)" }'));
instead?
I've run into this issue today and spent quite some time troubleshooting it. It seems to be related to the Access Token. When I use my App's Access Token to request the venue data, for some venues all I I get is the venue.name field. However, if I use the Graph API Explorer to generate a different token, I get the venue.id field as expected.
I went as far as to replace the Graph API Explorer's generated Access Token with my App Token, and sure enough all I received back was venue.name.

Resources