I'm trying to build some authorization into my Phalcon-based app. In my bootstrap file I instantiate my Auth class (which extends Component), and run my authorize() function. Inside that function I get the dispatcher by calling $Dispatcher = $this->di->getShared('dispatcher').
That all seems to run fine. However, when I then call $Dispatcher->getControllerName(), it returns NULL.
How do I access the controller name?
Here is my bootstrap file:
$Debug = new \Phalcon\Debug();
$Debug->listen();
#try{
# Create a Dependency Injection container
$DI = new \Phalcon\DI\FactoryDefault();
# Load config
$Config = require '../app/config/config.php';
$DI->setShared('config',$Config);
# Register an autoloader
$Loader = new \Phalcon\Loader();
$Loader->registerDirs($Config->phalcon->load_dirs->toArray());
$Loader->registerNamespaces($Config->phalcon->namespaces->toArray());
$Loader->register();
# Initialize Session
$Session = new \Phalcon\Session\Adapter\Files();
$Session->start();
$DI->setShared('session',$Session);
# Set up the View component
$DI->set('view',function() use($Config){
$View = new \Phalcon\Mvc\View();
$View->setViewsDir($Config->dir->views_dir);
$View->registerEngines(['.phtml'=> function($View,$DI) use ($Config){
$Volt = new \Phalcon\Mvc\View\Engine\Volt($View,$DI);
$Volt->setOptions([ 'compiledPath' => $Config->dir->views_compile_dir,
'compileAlways' => $Config->app->views_compile_always
]);
return $Volt;
}
]);
$View->Config = $Config;
return $View;
});
# Check authorization
$Auth = new Auth($DI);
if($Auth->authorize()){
$DI->setShared('user',$Auth->getUser());
}
else{
$DI->get('view')->render('system','notallowed');
exit();
}
# Set up connection to database
$DI->set('db',function() use($Config){
return new \Phalcon\DB\Adapter\Pdo\Mysql([ 'host' => $Config->database->host,
'dbname' => $Config->database->database,
'username' => $Config->database->username,
'password' => $Config->database->password
]);
});
# Set up base URL
$DI->set('url',function() use($Config){
$URL = new \Phalcon\Mvc\Url();
$URL->setBaseUri('/'.basename($Config->dir->app_dir_web));
return $URL;
});
# Set up message flashing to use session instead of direct
$DI->set('flash',function(){
return new \Phalcon\Flash\Session();
});
# Handle the requested URL
$App = new \Phalcon\Mvc\Application($DI);
# Echo the output
echo $App->handle()->getContent();
/*
}
catch(\Phalcon\Exception $e){
echo 'Phalcon Exception: ',$e->getMessage();
}
*/
I think that untill you call $app->handle() the dispatcher won't be properly setup.
Maybe not a direct response, but I've manage to successfully implement authorization using Vokuro app as a example: https://github.com/phalcon/vokuro.
Your bootstrap looks ok should work.
I'm using this in bootstrap file :
/**
* Handle the request
*/
$application = new \Phalcon\Mvc\Application();
$application->setDI($di);
if (!empty($_SERVER['REQUEST_URI'])) {
$uriParts = explode('?', $_SERVER['REQUEST_URI']);
$uri = $uriParts[0];
} else {
$uri = '/';
}
echo $application->handle($uri)->getContent();
As you can see there is $uri parameter passed to $application->handle() function.
Inside controllers: $this->dispatcher->getControllerName() works fine.
I am using a Router object so the following works for me before the ->handle call
/** #var $router \Phalcon\Mvc\Router */
$router = require APPLICATION_PATH.'/routes/default.php';
$router->handle($url);
$router->setUriSource(\Phalcon\Mvc\Router::URI_SOURCE_SERVER_REQUEST_URI);
/** #var $matched \Phalcon\Mvc\Router\Route */
$matched = $router->getMatchedRoute();
$paths = $matched->getPaths();
echo 'controller : ',$paths['controller'],"<br />";
echo 'action : ',$paths['action'],"<br />";
This is an old thread but in case people still need it, I will show you two ways to properly handle authorization.
In both cases, you should check authorization in a "before middleware", not anywhere else.
Checking authorization directly in your front controller is a bit premature.
1. By retrieving the handler, as you asked
Here you perform authorization checks only if needed. It is a bit more code, but a bit more effective too because you don't have to even use the database if you don't need to.
$app = \Phalcon\Mvc\Micro;
$app->before(function() use ($app)
{
/**
* #var \Phalcon\Mvc\Controller $oHandler
*/
$oHandler = null;
$sAction = null;
$aHandlerArr = (array)$app->getActiveHandler();
if (!empty($aHandlerArr) && !empty($aHandlerArr[1]))
{
$oHandler = $aHandlerArr[0];
$sAction = $aHandlerArr[1];
}
if ($oHandler && $oHandler->isAuthRequired($sAction))
{
// Do auth, and send an error if failed
}
});
2. Without voodoo magic
The other way is to simply try authorization in your before middleware without checking if you need it. It is the same code as above, but without the handler retrieval.
$app = \Phalcon\Mvc\Micro;
$app->before(function() use ($app)
{
// Do auth ...
// Set member in your app, if auth succeeded.
// Then in your controller actions, simply check for the member in your app
});
XD
$this->router->getActionName()
Related
I have a vuejs method which implements axios to send a put/create request over to my laravel api create method passing over some data.
create(data) {
this.mute = true;
window.axios.put('/api/showreels/create', {data}).then(({ data }) => {
this.showreels.push(new Showreel(data));
this.mute = false;
}).catch(error => {
document.write(error.response.data);
});
},
My api.php is setup with the following resource
//Showreel
Route::resource('/showreels' , 'ShowreelController' , [
'except' => ['edit', 'show', 'store']
]);
And I have a create method to handle the request and update persist the data. (Which I have added a load of debugging in)
/**
* Create a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create(Request $request)
{
$message = 'sdfsdfsdf';
$message = $message . $request->heading . 'BALLS';
\App::abort(500, $message);
$showreel = new Showreel();
$showreel->heading = $request->heading;
$showreel->subheading = $request->subheading;
$showreel->detail = $request->heading;
$showreel->youtubeid = $request->youtubeid;
$showreel->heading = "test";
$showreel->subheading = "test";
$showreel->detail = "test";
$showreel->youtubeid = "test";
$showreel->save();
return response($showreel->jsonSerialize(), Response::HTTP_CREATED);
}
However laravel is giving me this error.
Not sure why I am getting this error?
Looks like I had the STORE option disabled in my api.php which was closing down the post request option. The post request now takes me through to my store method in laravel.
Let say I have a laravel site on a host and another host just for storing some specific files. In second host I want to check if user is authenticated on laravel site and then I gave him/her access to the file. How can I do this?
You could pretty much just use normal PHP code for that, without having to touch the Laravel framework on your other server.
For example, create your own custom cookie or session variable that contains a token from authentication. Make the logic something like $authToken = md5($user->username) . "." . md5($user->password);, then just add it to a cookie or the $_SESSION[] variable.
From here, create a small backend on the other server that you use for storage, and do something like this.
<?php
/////////////
/**
* Here there should be database connection logic.
*/
/////////////
$posts = (object) $_POST;
if ($_SERVER['REQUEST_METHOD'] == "POST")
{
$postArray = [
'authToken',
'user'
];
foreach($postArray as $post)
{
if (!isset($_POST[$post]))
{
return false;
}
}
$query = htmlspecialchars("SELECT * FROM users WHERE id=" . $posts->user);
$result = mysqli_query($connection, $query);
$row = mysqli_fetch_all($result);
$db_stack = md5($row['username']) . "." . md5($row['password']);
if ($posts->authToken != $db_stack)
{
return false;
}
// Return the requested file here.
}
If (Auth::check()) $file = file_get_contents($urlToFile);
With Google API PHP client library I use the following code, which works well and prints lot of information about the user, who authorizes my application via OAuth2:
<?php
require_once('google-api-php-client-1.1.7/src/Google/autoload.php');
const TITLE = 'My amazing app';
const REDIRECT = 'https://example.com/myapp/';
session_start();
$client = new Google_Client();
$client->setApplicationName(TITLE);
$client->setClientId('REPLACE_ME.apps.googleusercontent.com');
$client->setClientSecret('REPLACE_ME');
$client->setRedirectUri(REDIRECT);
$client->setScopes(array(Google_Service_Plus::PLUS_ME));
$plus = new Google_Service_Plus($client);
if (isset($_REQUEST['logout'])) {
unset($_SESSION['access_token']);
}
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
error_log('The session state did not match.');
exit(1);
}
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
header('Location: ' . REDIRECT);
}
if (isset($_SESSION['access_token'])) {
$client->setAccessToken($_SESSION['access_token']);
}
if ($client->getAccessToken() && !$client->isAccessTokenExpired()) {
try {
$me = $plus->people->get('me'); # HOW TO SPECIFY FIELDS?
$body = '<PRE>' . print_r($me, TRUE) . '</PRE>';
} catch (Google_Exception $e) {
error_log($e);
$body = htmlspecialchars($e->getMessage());
}
# the access token may have been updated lazily
$_SESSION['access_token'] = $client->getAccessToken();
} else {
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$body = sprintf('<P>Login</P>',
$client->createAuthUrl());
}
?>
<!DOCTYPE HTML>
<HTML>
<HEAD>
<TITLE><?= TITLE ?></TITLE>
</HEAD>
<BODY>
<?= $body ?>
<P>Logout</P>
</BODY>
</HTML>
However I need less info than returned by the above script.
When entering just the fields I am interested in at the People: get "API explorer":
id,gender,name,image,placesLived
this again works well and prints only the specified fields:
MY QUESTION:
How to specify the fields in the above $me = $plus->people->get('me'); call?
After studying 1.1.7/src/Google/Service/Plus.php with the code:
/**
* Get a person's profile. If your app uses scope
* https://www.googleapis.com/auth/plus.login, this method is
* guaranteed to return ageRange and language. (people.get)
*
* #param string $userId The ID of the person to get the profile for. The
* special value "me" can be used to indicate the authenticated user.
* #param array $optParams Optional parameters.
* #return Google_Service_Plus_Person
*/
public function get($userId, $optParams = array())
{
$params = array('userId' => $userId);
$params = array_merge($params, $optParams);
return $this->call('get', array($params), "Google_Service_Plus_Person");
}
I have tried the following PHP code:
const FIELDS = 'id,gender,name,image,placesLived';
$me = $plus->people->get('me', array('fields' => urlencode(FIELDS)));
but for some reason it prints a lot of :protected strings:
Google_Service_Plus_Person Object
(
[collection_key:protected] => urls
[internal_gapi_mappings:protected] => Array
(
)
[aboutMe] =>
[ageRangeType:protected] => Google_Service_Plus_PersonAgeRange
[ageRangeDataType:protected] =>
[birthday] =>
[braggingRights] =>
[circledByCount] =>
[coverType:protected] => Google_Service_Plus_PersonCover
[coverDataType:protected] =>
[currentLocation] =>
[displayName] =>
[domain] =>
[emailsType:protected] => Google_Service_Plus_PersonEmails
[emailsDataType:protected] => array
[etag] =>
[gender] => male
...
Also I have tried just appending the fields after me:
$me = $plus->people->get('me?fields=' . urlencode(FIELDS)));
but get the 404 error:
Error calling GET
https://www.googleapis.com/plus/v1/people/me%3Ffields%3Did%252Cgender%252Cname%252Cimage%252CplacesLived:
(404) Not Found
UPDATE: I have created Issue #948 at GitHUb.
To specify which fields to get from the G+ API, you just have to specify a fields member in the options array. So actually you got very close to the solution:
$me = $plus->people->get('me', array('fields' => 'id,gender,name,image,placesLived'));
You don't even have to urlencode, as it is a default safety feature of the library itself.
The thing that might have tricked you is, that the Google_Service_Plus_Person class contains all the possible fields a protected members, not regarding the actual fields that were sent by the API. Not included fields will be empty in the object. As always, protected members should not be used in any way by the user of the class.
You, as the user of the library should only use public members, such as $me->getPlacesLived() and $me->getId(). Dumping whole objects is a nice tool during development, but in production calling the public interface is the way to go.
I use Laravel 5.1 and I am trying to intercept the default logger configuration (Monolog) and save logs to a different path that includes user name.
The current logs are saved to storage/logs/laravel.log.
The wanted paths are as follows
Authenticated Users: storage/logs/[username]/[date]_[api_path].log
Other user logs can be saved under storage/logs/guest/[date]_[api_path].log
ServiceProvider Approach
Have a LogServiceProvider and where I can modify each request and set the path as wanted.
public function boot(Request $request)
{
$log = new Logger('View Logs');
$user = \Auth::User()->getName(); // ERROR - uninitialized
$path = 'storage/logs/'.$user.'/mylogfile.log'; // doesn't matter API path
$log ->pushHandler(new StreamHandler($path, Logger::INFO));
...
}
The problem with this approach, the Auth::user() seems to be uninitialized.
Why does this happen and how do I solve this?
You might need to wrap it in an if statement, such as:
if ( Auth::check() ) {
// Do auth stuff
}
else {
// Do unauth stuff
}
Does that solve your problem?
This is because Auth has not been initialized and it is too early to call.
There is example way to solve:
Create log initializer, and call it from middleware (or from wherever you want):
class UserLogger
{
public function init()
{
$logger = new Logger('order');
$currUserId = Auth::id();
$logPath = storage_path('logs/by_user/' . $currUserId . '/' . Carbon::now()->toDateString() . '.log');
$logger->pushHandler(new StreamHandler($logPath, Logger::INFO));
return $logger;
}
}
//app\Http\Middleware\Authenticate.php
class Authenticate
{
public function handle($request, Closure $next, $guard = null)
{
$userLogger = new UserLogger();
$logger = $userLogger->init();
$context = ['some context data' => 'data'];
$logger->info('custom user actions', $context);
//...
Laravel 8 version can be viewed here
I am trying to implement my new payment method its working fine. But My requirement is little bit different. I need to redirect user to the payment gateway page. This is how I am trying to implement.
When user clicks on Place Order my Namespace_Bank_Model_Payment >> authorize method gets called. My gateway Says send an initial request, Based on details given gateway send a URL & Payment id. On this Url user must be redirected Where customer actually makes the payment. I have two actions in Controller success & error to handle the final response.
As, this code is getting called in an ajax request, I can't redirect user to another website. Can anybody guide me how to accomplish it?
Here is my code. I Have implemented getOrderPlaceRedirectUrl() method.
Here is my class::
<?php
class Namespace_Hdfc_Model_Payment extends Mage_Payment_Model_Method_Abstract
{
protected $_isGateway = true;
protected $_canAuthorize = true;
protected $_canUseCheckout = true;
protected $_code = "hdfc";
/**
* Order instance
*/
protected $_order;
protected $_config;
protected $_payment;
protected $_redirectUrl;
/**
* #return Mage_Checkout_Model_Session
*/
protected function _getCheckout()
{
return Mage::getSingleton('checkout/session');
}
/**
* Return order instance loaded by increment id'
*
* #return Mage_Sales_Model_Order
*/
protected function _getOrder()
{
return $this->_order;
}
/**
* Return HDFC config instance
*
*/
public function getConfig()
{
if(empty($this->_config))
$this->_config = Mage::getModel('hdfc/config');
return $this->_config;
}
public function authorize(Varien_Object $payment, $amount)
{
if (empty($this->_order))
$this->_order = $payment->getOrder();
if (empty($this->_payment))
$this->_payment = $payment;
$orderId = $payment->getOrder()->getIncrementId();
$order = $this->_getOrder();
$billingAddress = $order->getBillingAddress();
$tm = Mage::getModel('hdfc/hdfc');
$qstr = $this->getQueryString();
// adding amount
$qstr .= '&amt='.$amount;
//echo 'obj details:';
//print_r(get_class_methods(get_class($billingAddress)));
// adding UDFs
$qstr .= '&udf1='.$order->getCustomerEmail();
$qstr .= '&udf2='.str_replace(".", '', $billingAddress->getName() );
$qstr .= '&udf3='.str_replace("\n", ' ', $billingAddress->getStreetFull());
$qstr .= '&udf4='.$billingAddress->getCity();
$qstr .= '&udf5='.$billingAddress->getCountry();
$qstr .= '&trackid='.$orderId;
// saving transaction into database;
$tm->setOrderId($orderId);
$tm->setAction(1);
$tm->setAmount($amount);
$tm->setTransactionAt( now() );
$tm->setCustomerEmail($order->getCustomerEmail());
$tm->setCustomerName($billingAddress->getName());
$tm->setCustomerAddress($billingAddress->getStreetFull());
$tm->setCustomerCity($billingAddress->getCity());
$tm->setCustomerCountry($billingAddress->getCountry());
$tm->setTempStatus('INITIAL REQUEST SENT');
$tm->save();
Mage::Log("\n\n queryString = $qstr");
// posting to server
try{
$response = $this->_initiateRequest($qstr);
// if response has error;
if($er = strpos($response,"!ERROR!") )
{
$tm->setErrorDesc( $response );
$tm->setTempStatus('TRANSACTION FAILED WHILE INITIAL REQUEST RESPONSE');
$tm->save();
$this->_getCheckout()->addError( $response );
return false;
}
$i = strpos($response,":");
$paymentId = substr($response, 0, $i);
$paymentPage = substr( $response, $i + 1);
$tm->setPaymentId($paymentId);
$tm->setPaymentPage($paymentPage);
$tm->setTempStatus('REDIRECTING TO PAYMENT GATEWAY');
$tm->save();
// prepare url for redirection & redirect it to gateway
$rurl = $paymentPage . '?PaymentID=' . $paymentId;
Mage::Log("url to redicts:: $rurl");
$this->_redirectUrl = $rurl; // saving redirect rl in object
// header("Location: $rurl"); // this is where I am trying to redirect as it is an ajax call so it won't work
//exit;
}
catch (Exception $e)
{
Mage::throwException($e->getMessage());
}
}
public function getOrderPlaceRedirectUrl()
{
Mage::Log('returning redirect url:: ' . $this->_redirectUrl ); // not in log
return $this->_redirectUrl;
}
}
Now getOrderPlaceRedirectUrl() its getting called. I can see the Mage::log message. but the url is not there. I mean the value of $this->_redirectUrl is not there at the time of function call.
And one more thing, I am not planning to show customer any page like "You are being redirected".
Magento supports this type of payment gateway as standard and directly supports redirecting the user to a third party site for payment.
In your payment model, the one that extends Mage_Payment_Model_Method_Abstract, you'll need to implement the method:
function getOrderPlaceRedirectUrl() {
return 'http://www.where.should.we.pay.com/pay';
Typically you redirect the user to a page on your site, /mymodule/payment/redirect for example, and then handle the redirection logic in the action of the controller. This keeps your payment model clean and stateless, while allowing you to some some kind of "You are now being transferred to the gateway for payment" message.
Save everything you need to decide where to redirect to in a session variable, again typically Mage::getSingleton('checkout/session').
Magento have a pretty solid, if messy, implementation of this for Paypal standard. You can checkout how they do it in app/code/core/Mage/Paypal/{Model/Standard.php,controllers/StandardController.php}.
Hello guys here is solution.
In authorize function (see my code in above answer) change
$this->_redirectUrl = $rurl;
by Mage::getSingleton('customer/session')->setRedirectUrl($rurl);
& in function getOrderPlaceRedirectUrl() change it to like
public function getOrderPlaceRedirectUrl()
{
Mage::Log('returning redirect url:: ' . Mage::getSingleton('customer/session')->getRedirectUrl() );
return Mage::getSingleton('customer/session')->getRedirectUrl(); ;
}
after that code must be running & u'll be getting redirected to the third party gateway