I'm pretty new to the Zend framework, and am attempting to integrate an SSO with an existing Zend site. I have installed SimpleSamlPHP and can connect to the ADFS server and get assertion data in the form of First Name and Last Name using the following script
$lib = "/var/simplesamlphp";
$sp = "wte-sp";
try {
if(!file_exists("{$lib}/_autoload.php")) {
throw(new Exception("simpleSAMLphp lib loader file does not exist: ".
"{$lib}/_autoload.php"));
}
include_once("{$lib}/_autoload.php");
$as = new SimpleSAML_Auth_Simple($sp);
$as->requireAuth();
$valid_saml_session = $as->isAuthenticated();
} catch (Exception $e) {
throw(new Exception("SSO authentication failed: ". $e->getMessage()));
return;
}
if (!$valid_saml_session) {
try {
$as = new SimpleSAML_Auth_Simple($sp);
$as->requireAuth();
} catch (Exception $e) {
throw(new Exception("SSO authentication failed: ". $e->getMessage()));
return;
}
}
$attributes = $as->getAttributes();
print_r($attributes);
I am now attempting to move this into a Zend controller, on page load I am redirected to the SSO page for authentication and returned to the correct page with no problems, however, it appears as if the $attributes array is empty. I have confirmed that I have got the SAML cookie set, and I am seeing SAML data when I var_dump($_SESSION); but it looks as if somewhere along the line Zend is doing something unknown to the data as I'm always hitting the if (!$valid_saml_session) { statement and getting stuck in an authentication redirect loop.
As mentioned before, the code works perfectly as a standalone page, but not being too clued up on Zend, I'm drawing a bit of a blank. I have changed the last lines to
$attributes = $as->getAttributes();
$this->view->attributes = $attributes;
And have sent the results to the view, but the array is empty.
I am able to get the Login and Logout URLs to the view with no problems.
EDIT - 21/12/15
Looks like this is related to the way Zend is setting up sessions. In my Bootstrap.php file I have
protected function _initSession()
{
Zend_Session::setOptions(array(
'save_path' => $this->options['resources']['session']['save_path'],
'use_only_cookies' => 'on',
'remember_me_seconds' => 86400
));
Zend_Session::start();
}
If I comment this out, I get the SSO information back with no problems. I'm concerned about making this change as I'm not sure of the implications across the application, does anyone have a better solution to stop Zend from trashing my SSO session data?
Related
I have a LoginController where I do my usual login operation with combination of an email address and a password associated with the account.
I have separated my Hybridauth related code into a separate controller named OauthController where I have all my Hybridauth magic and where my callback / endpoint resides.
In the OauthController I check if user's email from the specified provider is already registered, and in either case I try to login that user with $this->Auth->setUser(object).
Whenever, or whatever from the $this->Auth is called, I get a response stating:
Session was already started
I have browser through CakePHP 3 code and found the following statement in:
vendor/cakephp/cakephp/src/Network/Session.php (335)
public function start()
{
if ($this->_started) {
return true;
}
if ($this->_isCLI) {
$_SESSION = [];
$this->id('cli');
return $this->_started = true;
}
if (session_status() === \PHP_SESSION_ACTIVE) {
throw new RuntimeException('Session was already started');
}
...
And that's the point in code where that message is thrown at me.
Now, as I browsed through the Hybridauth code itself, I have found following in:
vendor/hybridauth/hybridauth/src/Storage/Session.php (46)
public function __construct()
{
if (session_id()) {
return;
}
if (headers_sent()) {
throw new RuntimeException('HTTP headers already sent to browser and Hybridauth won\'t be able to start/resume PHP session. To resolve this, session_start() must be called before outputing any data.');
}
if (! session_start()) {
throw new RuntimeException('PHP session failed to start.');
}
}
And both of them call session_start, one before the other, although CakePHP's part is blocking me.
I have tried removing !session_start() check from Hybridauth, but then Hybridauth doesn't know where to read out it's thingies it needs to read.
So, as a demonstrator, I am trying to achieve this in OauthController:
<?php
namespace App\Controller;
use Hybridauth\Hybridauth;
class OauthController extends AppController
{
public function callback($provider)
{
try {
$hybridauth = new Hybridauth($config);
// additional mystery code
$hybridauth->authenticate();
if($everything_okay) {
$this->Auth->setUser($userObject); // and this is the point of failure
return $this->redirect('/account'); // and this never happends... :(
}
}
}
}
Any help, ideas, insights on how to deal with this are all welcome!
Simply start the CakePHP session manually before using the Hybridauth library, so that it bails out at the session_id() check and picks up the existing session.
For example in your controller:
$this->getRequest()->getSession()->start();
// in CakePHP versions before 3.6/3.5
// $this->request->session()->start();
I am experimenting with Drupal 8 as our customer websites. Our customers authenticate through our own authentication application at the moment which speaks to our document store (instead of MySQL) to authenticate a user and provide them with a unique session ID (JWT eventually but that's another day and conversation) which we can use to then query a REST API and get user data in any of our other self apps.
We are moving over from an old JSP based websites to drupal as our apps are now written in Symfony 3 but want our customer websites to be Drupal 8.
Here's the bit I am trying to work out. If I authenticate in our old website I want to be able to redirect to the Drupal 8 website with the session ID we have in our hand and use that to fetch a object back of our logged in user. I have this bit working fine but I now am in a position to say... Ok I have the user object back, the 3rd party service has said that session ID is valid so we know we are authenticated.
Please refer to the below flow chart. I want to be able to also authenticate in Drupal 8 manually. Is this possible (I am sure it is) and if so can someone point me in the right direction as to what I need/should be doing, API's I should be calling?
Thank you kindly and good day :)
You should use the External Auth module.
A good exemple of how use this module is the SimpleSamlPHP Auth
Ok so it turned out not to be that tricky in the end. I thought I would have to extend and implement various class and create my own provider (which is probably the best practice) but for KISS sake I found another way.
Create a user first if one does not exists based on the user data I get back from my external service. Then pass that created user to the user_login_finalize method (why are a lot of methods underscored Drupal...) which then authenticated my user.
public function inbound(Request $request)
{
// Point the guzzle client to our external session service.
$client = new GuzzleHttpClient([
'base_uri' => 'https://myexternalservice.com/apps/authentication/2/',
]);
// Attempt to send to request with the session ID from the parameters.
try {
$response = $client->request('GET', 'api/v1/user/' . $request->get('session_id'));
} catch (\Exception $e) {
throw new \HttpException($e->getMessage());
}
// Convert the response to an array.
$result = json_decode((string) $response->getBody(), true);
// Convert our array to a user entity.
if ($user = $this->convertResponseToUser($result['user'])) {
try {
// Attempt to load the user. If the user does not exist then create them first.
if (!$assumeUser = user_load_by_mail($user->getEmail())) {
// Create a Drupal user object.
$assumeUser = $this->createUser([
'name' => $user->getFirstName() . ' ' . $user->getLastName(),
'mail' => $user->getEmail()
]);
$assumeUser->save();
}
// Authenticate the user.
user_login_finalize($assumeUser);
} catch (\Exception $e) {
drupal_set_message(t('An unhandled exception occurred during authentication.'), 'error');
return $this->redirect('user.login');
}
}
return $this->redirect('mymodule.route');
}
I'm trying to implement the SimpleSAMLphp authentication tool in cakePHP.
I wrote a SamlAuthenticate component in app\Controller\Component\Auth which looks like this:
class SamlAuthenticate extends Component {
[...]
public function authenticate(CakeRequest $request, CakeResponse $response) {
$source = null;
$as = null;
if ($this->Session->check('Saml.source')) {
$source = $this->Session->read('Saml.source');
}
if ($source) {
require_once($this->settings['path'] . DS . 'lib' . DS . '_autoload.php');
$as = new SimpleSAML_Auth_Simple($source);
if(!$as->isAuthenticated()) {
$as->login();
} else {
return $as->getAttributes();
}
}
return false;
}
}
But I'm always getting an loop between the identity provider and my cake application.
I was wondering, if my server is the problem or I did something wrong with the configuration of the identity provider, so I wrote a simple test script and it worked without a problem:
require_once('/../simplesamlphp/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('facebook');
$as->requireAuth();
echo $as->isAuthenticated();
So, something in cakePHP breaks the authentication process. The SimpleSAMLAuthToken is set correctly (I can see that through the SimpleSAMLphp admin panel), but $as->isAuthenticated() always returns false.
I also tried https://github.com/bvidulich/CakePHP-simpleSAMLphp-Plugin with the same result.
maybe you are in a session conflict.
Take a look on the LostState info of the simpleSAMLphp documentation.
A fast workaround to see if that is your problem:
Configure the simplesamlphp to save the session on memcache. You will need to install a memcache server, the memcache php driver (remember to restart your apache after install ir) and then edit the config/config.php file of simpleSAMLphp and set
'store.type' => 'memcache',
Check that the simpleSAMLphp can write a session using the cookie extension of firefox. (Take a look on the session/cookie params of the config/config.php file.
i m trying to integrate an application build in zend into a website that is build in joomla.
when i try to set zend's auth it gives me an error "session has already been started by session.auto-start or session_start()" since i'm getting logged in user's details using joomla's functions
is it possible to have zend's auth in parallel with joomla's session?
if not then is it possible to get logged in user's information without using any of joomla's functions(i think this is not possible)
how do i use joomla's already started session into my zend application?
Could be a problem with the filename of the Session cookie. If you need seperated Session try to set the Session options:
Zend_Session::setOptions(array(
'name' => 'ZENDSession',
'save_path' => $appRoot . '/session',
'remember_me_seconds' => 864000
));
try
{
Zend_Session::start();
}
catch (Exception $e)
{
//Exception
}
If you want to Authenticate against the Joomla UserDatabase, try this Zend_Auth_Adapter:
http://www.plainlycode.com/Joomla/Joomla-Authentication-Adapter-for-the-Zend-Framework.html
I'm trying to use SOAP with C#. Magento 1.4.2.
http://localhost/api/v2_soap/?wsdl
Here I can see the method catalogProductCreate
So I try to connect with:
$proxy = new SoapClient('http://localhost/api/v2_soap/?wsdl');
$sessionId = $proxy->login('xxx', 'xxxxxx'); // user with full access
$newProductData = new stdClass();
$newProductData->name = 'Product Name';
$newProductData->description = 'Description';
$newProductData->short_description = 'Short Description';
$newProductData->websites = array(138);
$newProductData->categories = array(7,15);
$newProductData->status = 1;
$newProductData->price = 45;
$newProductData->tax_class_id = 2;
$newProductData->weight = 1;
$result = $proxy->catalogProductCreate(
$sessionId, // Soap Session
'simple', // Product Type
4, // Attribute Set Id (Default)
'product-sku', // Product Sku
$newProductData // Product Data
);
But I receive this output:
Fatal error: Uncaught SoapFault exception: [4] Resource path is not callable.
(details are Magento 1.6.x specific, but techniques, if not details, should be applicable to other versions)
I'm assuming, based on your code sample, that you're using PHP client code to test for the existence of a method, which you can then apply to a call from your C# application?
Assuming that's the case, it means you know PHP, so you'll want to debug this at the Magento soap server PHP level. The only class file that produces that fault is
app/code/core/Mage/Api/Model/Server/Handler/Abstract.php
Either add the following logging temporarily and directly to that file, or drop a copy of the class file in
app/code/local/Mage/Api/Model/Server/Handler/Abstract.php
for a codepool override.
Look in that class file for the following exception
throw new Mage_Api_Exception('resource_path_not_callable')
This is what causes the Magento soap server to response with that fault. There are four places this happens in that file. Add logging calls above each one.
Mage::Log(sprintf('Line %s in file %s',__LINE__, __FILE__));
throw new Mage_Api_Exception('resource_path_not_callable');
This will let you know which fault is causing your problem, from which you can debug and log further. There are two places this can happen (four total in the file, one for a regular call, another for the multi-call).
In order of appearance, with possible causes in the comments.
//here magento is attempting to instantiate the "API Model" that will perform
//the work of your API call. Upon instantiation, it discovers that the model
//doesn't inherit from Mage_Api_Model_Resource_Abstract, and bails.
//This is rare for a non-custom API call, but might be caused by a class rewrite
//gone amuck, or a very hacked system
try {
$model = Mage::getModel($modelName);
if ($model instanceof Mage_Api_Model_Resource_Abstract) {
$model->setResourceConfig($resources->$resourceName);
}
} catch (Exception $e) {
Mage::Log(sprintf('Line %s in file %s',__LINE__, __FILE__));
throw new Mage_Api_Exception('resource_path_not_callable');
}
//Here Magento's been able to instantiate the $model, and is checking if the method is
//callable. If not, it bails. Again, for a standard, stock API call this shouldn't
//be happening, but could be the result of a rewrite gone wrong, or someone hacking an
//api class to make the method non accesible, or someone hacking the method mapping in api.xml
if (is_callable(array(&$model, $method))) {
if (isset($methodInfo->arguments) && ((string)$methodInfo->arguments) == 'array') {
return $model->$method((is_array($args) ? $args : array($args)));
} elseif (!is_array($args)) {
return $model->$method($args);
} else {
return call_user_func_array(array(&$model, $method), $args);
}
} else {
Mage::Log(sprintf('Line %s in file %s',__LINE__, __FILE__));
throw new Mage_Api_Exception('resource_path_not_callable');
}
Figure out why Magento is throwing the API error. It will often point to a type in your soap call, OR point you towards what's been hacked in your PHP system
Making sure that you can she the wsdl resource is correct, but i also ran into that issue when i didnt have the user set up to the correct permissions under the role.
Try to create a webservice user with role and assigned them to a role that has access to ‘ALL’. option in role resources menu in role information.
Put this file into root folder of magento/project so that you can access all the method of magento.
Enjoy the idea...