I'm using Codeigniter 1.7. Does anyone have any experience of creating web services with PHP, particularly within the CodeIgniter framework? What are security measures need to consider while implementing web services? How to provide authentication with API keys?
Any Ideas?
It depends on the kind of web service you are inquiring about. Is the web service going to be a daemon for example? or a typical online web service. For either of these you must implement a RESTful type. RESTful meaning a stateless connection. This is where API keys are used; to identity a user for example.
Luckily Codeigniter is one with many libraries and extensions. An example of such libraries can be here: https://github.com/philsturgeon/codeigniter-restserver
Now for security concerns: API keys would replace sessions or any state. You would have to make full checks on the api. Many sites that implement APIs offer different solutions to the same end result.
Authentication with API keys are simple. You would check it against a storage type(database).
Here is a tutorial using codeigniter and the library linked previously: http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/
This might be somewhat vague, but since you dont have any specific problems or apparent needs its hard to be specific.
EDIT:
In that case it would be better implementing a RESTful interface so that your iphone app can also use all of the user functionalities that your service provides. The best way would be to make everything accessible in one way. Meaning not having different controllers / models for the iphone connections and web connections.
So for example you could have the following controller:
<?php
class Auth extends CI_Controller{
public function login(){
//Check if their accessing using a RESTful interface;
$restful = $this->rest->check();
if($restful){
//Check for the API keys;
$apiKey = $this->input->get('apiKey');
$secretKey = $this->input->get('secretKey');
//If you have any rules apon the keys you may check it (i.e. their lengths,
//character restrictions, etc...)
if(strlen($apiKey) == 10 and strlen($secretKey) == 14)
{
//Now check against the database if the keys are acceptable;
$this->db->where('apiKey', $apiKey);
$this->db->where('secretKey', $secretKey);
$this->db->limit(1);
$query = $this->db->get('keys');
if($this->db->count_all_results() == 1)
{
//It's accepted the keys now authenticate the user;
foreach ($query->result() as $row)
{
$user_id = $row->user_id;
//Now generate a response key;
$response_key = $this->somemodel->response_key($user_id);
//Now return the response key;
die(json_encode( array(
'response_key' => $response_key,
'user_id' => $user_id
)
)
);
} //End of Foreach
}//End of Result Count
}//End of length / character check;
} else {
//Perform your usual session login here...;
}
}
}
?>
Now this is just a small example for performing these types of requests. This could apply to any type of controller. Though there are a few options here. You could make every request pass the apikey, and the secret each time and verify it at each request. Or you could have some sort of whitelist that once you have been verified the first time each request after that would be whitelisted, and or black listed on the opposite.
Hope this helps,
Daniel
<?php
//First Create Api file in controller name Api.php
/*
api call in postman
login :
email , password
http://localhost/demo/api/login
https://prnt.sc/pbs2do
register (user): :
fullname , email , password , recipeunit
http://localhost/demo/api/signup
https://prnt.sc/pbs3cc
profile and list (user profile and all user ) :
View Profile : email, if all then pass blank
http://localhost/demo/api/userlist
change password :
http://localhost/demo/api/change_password
email ,password ,newpassword , conformnewpassword (if needed)
https://prnt.sc/pbs3rt
*/
if(!defined('BASEPATH')) exit('No direct script access allowed');
require APPPATH . '/libraries/BaseController.php'; // this file will download first and pest in library
class Api extends BaseController
{
/**
* This is default constructor of the class
*/
public function __construct()
{
parent::__construct();
$this->load->model('api/signup_model','signup_model');
}
/**
* Index Page for this controller.
*/
public function index()
{
}
public function signup()
{
$this->signup_model->signup();
}
public function login()
{
$this->signup_model->login();
}
public function userlist()
{
$this->signup_model->userlist();
}
public function edit_user()
{
$this->signup_model->edit_user();
}
public function change_password()
{
$this->signup_model->change_password();
}
public function testpass()
{
$this->signup_model->testpass();
}
}
// then create model in model folder create api folder create signup_model.php file
//after that
if (!defined('BASEPATH')) exit('No direct script access allowed');
class Signup_model extends CI_Model {
public function __construct()
{
parent::__construct();
$this->load->database(); /* load database library */
}
// User register (signin) process
public function signup($data = array())
{
// another db field update like dt_createddate
if(!array_key_exists('dt_createddate', $data)){
$data['dt_createddate'] = date("Y-m-d H:i:s");
}
if(!array_key_exists('dt_updateddate', $data)){
$data['dt_updateddate'] = date("Y-m-d H:i:s");
}
if(!array_key_exists('dt_updateddate', $data)){
$data['dt_updateddate'] = date("Y-m-d H:i:s");
}
$data['var_fullname'] = $this->input->post('fullname');
$data['var_email'] = $this->input->post('email');
$data['var_password'] =getHashedPassword($this->input->post('password')) ;
$data['int_recipeunit'] = $this->input->post('recipeunit');
// if(!empty($data['var_fullname']) && !empty($data['var_email']) && !empty($data['var_password']) ){ }
/* check emailid all ready exist or not */
$email_check=$this->input->post('email');
$this->db->select('var_email');
$this->db->from('tbl_user');
$this->db->where('var_email', $email_check);
$query = $this->db->get();
$user = $query->result();
if(!empty($user))
{
echo "{\"status\" : \"404\",\"message\" : \"Email all ready register\",\"data\":".str_replace("<p>","",'{}'). "}";
}
else
{
$insert = $this->db->insert('tbl_user', $data);
if($insert){
$this->db->select('var_email as email,var_fullname as fullname,dt_createddate as createdate');
$insert_id = $this->db->insert_id();
$query = $this->db->get_where('tbl_user', array('int_id' => $insert_id));
echo "{\"status\" : \"200\",\"message\" : \"User added sucessfully\",\"data\":".str_replace("<p>","",json_encode($query->row_array())). "}";
// return $this->db->insert_id();
}else
{
$message="Something Wrong";
echo "{\"status\" : \"400\",\"data\":".str_replace("<p>","",json_encode($message)). "}";
// return false;
}
}
}
/* Login user $email, $password*/
function login()
{
$email=$this->input->post('email');
$password=$this->input->post('password');
$this->db->select('int_id,var_email,var_password');
$this->db->from('tbl_user');
$this->db->where('var_email', $email);
$this->db->where('chr_status', 'A');
$query = $this->db->get();
$user = $query->result();
if(!empty($user))
{
if(verifyHashedPassword($password, $user[0]->var_password))
{
$this->db->select('var_email as email,var_fullname as fullname,dt_createddate as createdate');
$query = $this->db->get_where('tbl_user', array('var_email' => $email));
echo "{\"status\" : \"200\",\"message\" : \"Login sucessfully\",\"data\":".str_replace("<p>","",json_encode($query->row_array())). "}";
}
else
{
echo "{\"status\" : \"404\",\"message\" : \"Password does not match\",\"data\":".str_replace("<p>","",'{}'). "}";
}
}
else
{
echo "{\"status\" : \"404\",\"message\" : \"Invalid email \",\"data\":".str_replace("<p>","",'{}'). "}";
}
}
/* Fetch user data all or single */
function userlist()
{
$email=$this->input->post('email'); // post id of which user data you will get
if(!empty($email))
{
$email=$this->input->post('email');
$password=$this->input->post('password');
$this->db->select('int_id,var_email,var_password');
$this->db->from('tbl_user');
$this->db->where('var_email', $email);
$this->db->where('chr_status', 'A');
$query = $this->db->get();
$user = $query->result();
if(!empty($user))
{
$this->db->select('var_email as email,var_fullname as fullname,dt_createddate as createdate');
$query = $this->db->get_where('tbl_user', array('var_email' => $email));
$responce_json=json_encode($query->row_array());
echo "{\"status\" : \"200\",\"message\" : \"User data\",\"data\":".str_replace("<p>","",$responce_json). "}";
}
else
{
echo "{\"status\" : \"404\",\"message\" : \"Invalid email \",\"data\":".str_replace("<p>","",'{}'). "}";
}
}
else
{
$this->db->select('var_email as email,var_fullname as fullname,dt_createddate as createdate');
$query = $this->db->get('tbl_user');
$responce_json=json_encode($query->result_array());
echo "{\"status\" : \"200\",\"message\" : \"User data\",\"data\":".str_replace("<p>","",$responce_json). "}";
}
}
/* Update user data */
function edit_user($data = array()) {
$id = $this->input->post('id');
$data['first_name'] = $this->input->post('first_name');
/* $data['last_name'] = $this->input->post('last_name');
$data['email'] = $this->input->post('email');
$data['phone'] = $this->input->post('phone'); */
if(!empty($data) && !empty($id)){
if(!array_key_exists('modified', $data)){
$data['modified'] = date("Y-m-d H:i:s");
}
$update = $this->db->update('users', $data, array('id'=>$id));
if($update){
$message="User Update Sucessfully";
$responce_json=json_encode($message);
echo "{\"status\" : \"200\",\"data\":".str_replace("<p>","",$responce_json). "}";
}
}
else
{
return false;
}
}
/* change password */
function change_password()
{
$email=$this->input->post('email');
$password=$this->input->post('password');
$newpassword=$this->input->post('newpassword');
//$conformnewpassword=$this->input->post('conformnewpassword');
$this->db->select('int_id,var_email,var_password');
$this->db->from('tbl_user');
$this->db->where('var_email', $email);
$this->db->where('chr_status', 'A');
$query = $this->db->get();
$user = $query->result();
if(!empty($user))
{
if(verifyHashedPassword($password, $user[0]->var_password))
{
//if($newpassword==$conformnewpassword)
//{
$data['var_password'] = getHashedPassword($newpassword);
$update = $this->db->update('tbl_user', $data, array('var_email'=>$email));
$this->db->select('var_email as email,var_fullname as fullname,dt_createddate as createdate');
$query = $this->db->get_where('tbl_user', array('var_email' => $email));
echo "{\"status\" : \"200\",\"message\" : \"Password change sucessfully\",\"data\":".str_replace("<p>","",json_encode($query->row_array())). "}";
/* }
else
{
echo "{\"status\" : \"404\",\"message\" : \"New pass and conform pass does not match \",\"data\":".str_replace("<p>","",'{}'). "}";
} */
}
else
{
echo "{\"status\" : \"404\",\"message\" : \"Invalid old password \",\"data\":".str_replace("<p>","",'{}'). "}";
}
}
else
{
echo "{\"status\" : \"404\",\"message\" : \"Invalid email \",\"data\":".str_replace("<p>","",'{}'). "}";
}
}
/*
* Delete user data
*/
/* public function delete($id){
$delete = $this->db->delete('users',array('id'=>$id));
return $delete?true:false;
} */
}
?>
Related
I'm trying to create a custom connection where I should use web service. So I read the tutorial on security and this one on custom provider. Now I'm trying to create my own login form with 3 fields : Email, password and number. After validation I understood that my /login_check pass in the function loadUserByUsername($username), but this function took in argument just $username and doesn't take my fields email and number. To execute my web service I need to get my 3 args. How can I customize my login form?
The goal is: When users submit the login form I want to send a web service with login form args. If I get my response without error I want to connect my user loaded by web service to symfony2 toolbar else I want to display an error message.
You can see my code here :
Security.yml :
security:
encoders:
MonApp\MonBundle\Security\User\WebserviceUser: sha512
#Symfony\Component\Security\Core\User\User: plaintext
# http://symfony.com/doc/current/book/security.html#hierarchical-roles
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
# http://symfony.com/doc/current/book/security.html#where-do-users-come- from-user-providers
providers:
#in_memory:
#memory:
#users:
#ryan: { password: ryanpass, roles: 'ROLE_USER' }
#admin: { password: kitten, roles: 'ROLE_ADMIN' }
webservice:
id: webservice_user_provider
# the main part of the security, where you can set up firewalls
# for specific sections of your app
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
area_secured:
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
default_target_path: /test
logout:
path: /logout
target: /
# with these settings you can restrict or allow access for different parts
# of your application based on roles, ip, host or methods
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_AUTHENTICATED }
WebserviceUser.php :
<?php
namespace MonApp\MonBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
class WebserviceUser implements UserInterface
{
private $email;
private $password;
private $num;
private $salt;
private $roles;
public function __construct($email, $password, $num, $salt, array $roles)
{
$this->email = $email;
$this->password = $password;
$this->num = $num;
$this->salt = $salt;
$this->roles = $roles;
}
public function getUsername()
{
return '';
}
public function getEmail()
{
return $this->email;
}
public function getPassword()
{
return $this->password;
}
public function getNum()
{
return $this->num;
}
public function getSalt()
{
return $this->salt;
}
public function getRoles()
{
return $this->roles;
}
public function eraseCredentials()
{}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof WebserviceUser) {
return false;
}
if ($this->email !== $user->getEmail()) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->num !== $user->getNum()) {
return false;
}
if ($this->getSalt() !== $user->getSalt()) {
return false;
}
return true;
}
}
WebserviceUserProvider.php
<?php
namespace MonApp\MonBundle\Security\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use MonApp\MonBundle\Security\User\WebserviceUser;
class WebserviceUserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
//print_r($username);
//die();
// effectuez un appel à votre service web ici
return new WebserviceUser('email', 'password', '45555', 'salt', array('ROLE_USER'));
//throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof WebserviceUser) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
print_r($user);
die();
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'MonApp\MonBundle\Security\User\WebserviceUser';
}
}
service.yml
parameters:
webservice_user_provider.class: MonApp\MonBundle\Security\User\WebserviceUserProvider
services:
webservice_user_provider:
class: "%webservice_user_provider.class%"
I won't put all the code, but my login action, template and routing are exactly the same than security link. But my user new WebserviceUser('email', 'password', '45555', 'salt', array('ROLE_USER')) isn't connected to the toolbar. So I think I forgot something...
Do I need to use a Listener, UserToken and Factory to do that ?
Ok boy, prepare for a long answer.
I assume that you have a folder named Security placed in /MonApp/MonBundle
First you need a custom Token placed in Security/Token/WebServiceToken
<?php
namespace MonApp\MonBundle\Security\Token;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class WebServiceToken implements TokenInterface
{
protected $attributes;
protected $authenticated;
protected $user;
public function __construct($attributes)
{
$this->setAttributes($attributes);
$this->authenticated = false;
$this->user = null;
}
/**
* {#inheritdoc}
*/
public function serialize()
{
return serialize(
array(
is_object($this->user) ? clone $this->user : $this->user,
$this->authenticated,
$this->attributes
)
);
}
/**
* {#inheritdoc}
*/
public function unserialize($serialized)
{
list($this->user, $this->authenticated, $this->attributes) = unserialize($serialized);
}
public function __toString()
{
$result = '';
foreach($this->attributes as $name => $value)
{
$result .= "$name: $value ";
}
return "Token($result)";
}
/**
* Returns the user roles.
*
* #return RoleInterface[] An array of RoleInterface instances.
*/
public function getRoles()
{
return $this->user->getRoles();
}
public function getUser()
{
return $this->user;
}
public function setUser($user)
{
$this->user = $user;
}
public function getUsername()
{
return $this->user->getUsername();
}
public function isAuthenticated()
{
return $this->authenticated;
}
public function setAuthenticated($isAuthenticated)
{
$this->authenticated = $isAuthenticated;
}
public function eraseCredentials()
{
;
}
public function getAttributes()
{
return $this->attributes;
}
public function setAttributes(array $attributes)
{
$this->attributes = $attributes;
}
public function hasAttribute($name)
{
return array_key_exists($name, $this->attributes);
}
public function getAttribute($name)
{
if (!array_key_exists($name, $this->attributes)) {
throw new \InvalidArgumentException(sprintf('This token has no "%s" attribute.', $name));
}
return $this->attributes[$name];
}
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
}
public function getCredentials()
{
return null;
}
}
Then you need a Firewall in Security/Authentication/WebServiceAuthenticationListener
<?php
namespace MonApp\MonBundle\Security\Authentication;
use MonApp\MonBundle\Security\Token\WebServiceToken;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
class WebServiceAuthenticationListener implements ListenerInterface
{
protected $securityContext;
protected $authentificationManager;
protected $logger;
/**
* #param SecurityContextInterface $securityContext
* #param AuthenticationManagerInterface $authenticationManager
* #param LoggerInterface $logger
*/
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
$this->logger = $logger;
}
/**
* {#inheritdoc}
* #see \Symfony\Component\Security\Http\Firewall\ListenerInterface::handle()
*/
final public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
/**
* Fill $attributes with the data you want to set in the user
*/
$attributes = array();
$token = new WebServiceToken($attributes);
try {
if (null !== $this->logger ) {
$this->logger->debug(sprintf('Vérification du contexte de sécurité pour le token: %s', $token));
}
$token = $this->authenticationManager->authenticate($token);
if (null !== $this->logger) {
$this->logger->info(sprintf('Authentification réussie: %s', $token));
}
// Token authentifié
$this->securityContext->setToken($token);
}
catch (AuthenticationException $failed) {
throw $failed;
}
}
}
Then you need an Authentication provider in Security/Authentication/WebServiceAuthenticationProvider
<?php
namespace MonApp\MonBundle\Security\Authentication;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use MonApp\MonBundle\Security\User\WebServiceUser;
use MonApp\MonBundle\Security\User\WebServiceUserProvider;
use MonApp\MonBundle\Security\Token\WebServiceToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
class WebServiceAuthenticationProvider implements AuthenticationProviderInterface
{
protected $provider;
public function __construct(WebServiceUserProvider $provider)
{
$this->provider = $provider;
}
public function authenticate(TokenInterface $token)
{
if (!$this->supports($token)) {
return new AuthenticationException('Token non supporté');
}
$user = $this->provider->createUser($token->getAttributes());
$token->setUser($user);
/**
* CALL TO THE WEB SERVICE HERE
*/
$myCallisASuccess = true;
if($myCallisASuccess) {
$token->setAuthenticated(true);
}
return $token;
}
public function supports(TokenInterface $token)
{
return $token instanceof WebServiceToken;
}
}
Now the factory ... Security/Factory/WebServiceFactory
<?php
namespace MonApp\MonBundle\Security\Factory;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
class WebServiceFactory implements SecurityFactoryInterface
{
/**
* {#inheritdoc}
* #see \Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface::create()
*/
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.web_service'.$id;
$container->setDefinition($providerId, new DefinitionDecorator('web_service.security.authentication.provider'));
$listenerId = 'security.authentication.listener.web_service.'.$id;
$container->setDefinition($listenerId, new DefinitionDecorator('web_service.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
/**
* {#inheritdoc}
* #see \Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface::getPosition()
*/
public function getPosition()
{
return 'pre_auth';
}
/**
* {#inheritdoc}
* #see \Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface::getKey()
*/
public function getKey()
{
return 'web_service';
}
}
You have to edit WebServiceUserProvider by adding this function
public function createUser(array $attributes)
{
$email = $attributes['email'];
$password = $attributes['password'];
$num = $attributes['num'];
$salt = $attributes['salt'];
$user = new WebServiceUser($email, $password, $num, $salt);
return $user;
}
And remove $roles from you WebServiceUSer class:
public function __construct($email, $password, $num, $salt)
{
$this->email = $email;
$this->password = $password;
$this->num = $num;
$this->salt = $salt;
$this->roles = array();
}
Ok, now you have all you security classes done. Let's configure this....
In the class MonBundle
<?php
namespace MonApp\Bundle\MonBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use MonApp\Bundle\MonBundle\Security\Factory\WebServiceFactory;
class MonBundle extends Bundle
{
/**
* {#inheritdoc}
* #see \Symfony\Component\HttpKernel\Bundle\Bundle::build()
*/
public function build(ContainerBuilder $container)
{
parent::build($container);
// Ajout de la clef 'web_service' à l'extension security
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new WebServiceFactory());
}
}
In the config of MonBundle
services:
web_service.security.user.provider:
class: MonApp\Bundle\MonBundle\Security\User\WebServiceUserProvider
web_service.security.authentication.listener:
class: MonApp\Bundle\MonBundle\Security\Authentication\WebServiceAuthenticationListener
arguments: ['#security.context', '#web_service.security.authentication.provider','#?logger']
web_service.security.authentication.provider:
class: MonApp\Bundle\MonBundle\Security\Authentication\WebServiceAuthenticationProvider
arguments: ['#web_service.security.user.provider']
And last, in your app config:
security:
area_secured:
pattern: ^/
web_service: ~
form_login:
login_path: /login
check_path: /login_check
default_target_path: /test
logout:
path: /logout
target: /
I have installed php unit in my local server, but I dont understand (reading the php unit help) how to test my action create. My action is this, and the only thing I want to test is if it saves on database.
/**
* Creates a new model.
* If creation is successful, the browser will be redirected to the 'view' page.
*/
public function actionCreate() {
$_class = $this->getClassName();
$model = new $_class;
if (isset($_POST)) {
$model->attributes = $_POST;
$this->armaMensajeABMGrilla($model->save(), $model);
}
$this->renderPartial('create', array(
'model' => $model,), false, true);
}
protected function armaMensajeABMGrilla($guardoOk, $modelo = null) {
if ($guardoOk == true) {
$this->respuestaJSON = array('success' => true, 'mensaje' => 'ok');
} else {
$erroresMensaje = 'Listado de Errores: <br/><ul>';
$i = 0;
if (isset($modelo->errors)) {
foreach ($modelo->errors as $error) {
$erroresMensaje .= '<li>Error(' . $i . '): ' . $error[0] . '</li>';
$i++;
}
$erroresMensaje.='</ul>';
}
$this->respuestaJSON = array('success' => false, 'mensaje' => $erroresMensaje);
}
$this->renderJSON($this->respuestaJSON);
}
How will be the test method? something like this?
public function actionCreateTest(){
$model = new Model;
$this->asserttrue($model->save());
}
write functional tests for testing controllers functionality instead of unit tests,also
the thing that you are asserting here
$this->assertEquals(true,$controller->actionCreate());
if the outcome of $controller->actionCreate() is the value true, which is not!
you are $this->renderPartial() in that and returning nothing, so that statement will never be true.
i have problem to create web service in cake php 2.3 , i'm use nusoap lib in my web site
i have error in web service output
simple code :
output :
Error
Response not of type text/xml: text/html
WebservicesController :
App::uses('AppController', 'Controller');
App::uses('Sanitize', 'Utility');
ini_set('soap.wsdl_cache_enabled', 0);
class WebservicesController extends AppController{
var $components = array('RequestHandler');
var $helpers = array('Text', 'Xml');
public $name = 'Webservices';
public $useTable = false;
public $uses = array();
public $autoRender = false;
public $layout = false;
function process()
{
Configure::write('debug',0);
Configure::write('Session.start', false);
App::import('Vendor', 'nusoap',array('file'=>'nusoap'.DS.'lib'.DS.'nusoap.php'));
$server = new soap_server();
$endpoint = 'http://localhost/mysite/webservices/process';
//initialize WSDL support
$server->configureWSDL('helloWorldwsdl', 'urn:helloWorldwsdl', $endpoint);
$server->soap_defencoding='UTF-8';
$server->decode_utf8 = false;
$this->RequestHandler->respondAs('xml');
//$this->layoutPath = 'xml';
$server->register('helloWorld', // method name
array('return' => 'xsd:string'), // output parameters
'urn:helloWorldwsdl', // namespace
'urn:helloWorldwsdl#helloWorld', // soapaction
'rpc', // style
'encoded', // use
'Says hello to the caller' // documentation
);
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
$this->autoRender = false;
exit();
}
function helloWorld() {
return 'Hello';
}
}
client code :
require_once('lib/nusoap.php');
// Create the client instance
$client = new nusoap_client('http://localhost/mysite/webservices/process?wsdl');
$client->soap_defencoding='UTF-8';
$client->decode_utf8 = false;
// Check for an error
$err = $client->getError();
if ($err) {
// Display the error
echo '<h2>Constructor error</h2><pre>' . $err . '</pre>';
// At this point, you know the call that follows will fail
}
//======================================================
// Call the SOAP method
$result = $client->call('helloWorld') ;
// Check for a fault
if ($client->fault) {
echo '<h2>Fault</h2><pre>';
print_r($result);
echo '</pre>';
} else {
// Check for errors
$err = $client->getError();
if ($err) {
// Display the error
echo '<h2>Error</h2><pre>' . $err . '</pre>';
} else {
// Display the result
echo '<h2>Result</h2><pre>';
print_r($result);
echo '</pre>';
}
}
I tried your example and it worked properly adding an empty array when recording method "helloworld" in the soap server
eg:
$server->register('helloWorld', // method name
array(), //input parameters
array('return' => 'xsd:string'), // output parameters
'urn:helloWorldwsdl', // namespace
'urn:helloWorldwsdl#helloWorld', // soapaction
'rpc', // style
'encoded', // use
'Says hello to the caller' // documentation
I'm currently dealing with Symfony2's Security component.
I try to authenticate users against a webservice. To authenticate a user, I have to provide to the webservice a username and a password.
I know that I've got to create a class that implements UserProvider. But the loadUserByUsername function doesn't fit my webservice needs : in order to authenticate a user, it ask for both username and password whereas the UserProvider's function only requires username.
Here is a similar question to the problem I face : Symfony2 authentication without UserProvider
I've been struggling on this problem for a couple of days...
I fixed this problem in that way:
services.yml:
services:
user_provider:
class: "%my_class%"
arguments: ["#service_container"]
WebServiceUserProvider.php
/**
* #param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->apiClient = $container->get('api_client');
$this->request = $container->get('request');
}
and use $password = $this->request->get('password'); in your loadUserByUsername method
One way of accomplishing this would be to load the user by the username and then validate the password. If the a user exists for the given username and the password entered matches with the password of that user, then authenticate the user.
Example:
public function userLogin($username, $password)
{
$em = $this->getEntityManager();
$query = $em->createQuery('SELECT u FROM VenomCoreBundle:User u WHERE u.username = :username OR u.email = :username AND u.isDeleted <> 1 ')
->setParameter('username', $username);
try {
$entity = $query->getSingleResult();
if (count($entity) > 0) {
$encoder = new MessageDigestPasswordEncoder('sha512', true, 10);
$passwordEnc = $encoder->encodePassword($password, $entity->getSalt());
$userPassword = $entity->getPassword();
if ($passwordEnc == $userPassword) {
$tokenValue = $entity->getUserToken();
$profile = $entity->getUserProfile();
if(!$profile) {
return false;
}
$userName = $profile->getFullName();
$response = array(
'token' => $tokenValue,
'username' => $userName
);
} else {
return false;
}
}
} catch (\Doctrine\Orm\NoResultException $e) {
return false;
}
return $response;
}
I have been getting into Unit Testing with Zend Framework. I am getting used to the other things it provide but I am having a hard time understanding Mock Objects.
For this example, I am trying to use a Mock Object to test out my model.
<?php
class Twitter_Model_Twitter
{
private $_twitter;
/**
* Make the options injectable.
* __contruct($auth, $key)
*/
public function __construct()
{
$config = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV);
$key = $config->encryption->salt;
$iv_size = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$password = mcrypt_decrypt(MCRYPT_XTEA, $key, $password, MCRYPT_MODE_ECB, $iv);
$this->_twitter = new Zend_Service_Twitter($username, $password);
}
public function verifyCredentials()
{
return $this->_twitter->account->verifyCredentials();
}
public function friendsTimeline($params)
{
return $this->_twitter->status->friendsTimeline($params);
}
}
For my unit test:
require_once ('../application/models/Twitter.php');
class Model_TwitterTest extends ControllerTestCase
{
/**
* #var Model_Twitter
*/
protected $_twitter;
public function testfriendsTimeline()
{
$mockPosts = array('foo', 'bar');
//my understanding below is:
//get a mock of Zend_Service_Twitter with the friendsTimeline method
$twitterMock = $this->getMock('Zend_Service_Twitter', array('friendsTimeline'));
/*
line above will spit out an error:
1) testfriendsTimeline(Model_TwitterTest)
Missing argument 1 for Mock_Zend_Service_Twitter_9fe2aeaa::__construct(), called in
/Applications/MAMP/bin/php5/lib/php/PHPUnit/Framework/TestCase.php on line 672 and
defined /htdocs/twitter/tests/application/models/TwitterTest.php:38
*/
$twitterMock->expects($this->once())
->method('friendsTimeline')
->will($this->returnValue($mockPosts));
$model = new Twitter_Model_Twitter();
$model->setOption('twitter', $twitterMock);
$posts = $model->friendsTimeline(array('count'=>20));
$this->assertEquals($posts, $mockPosts);
}
}
How would you test the following?
1) verifyCredentials()
2) friendsTimeline()
Thanks,
Wenbert
I am going to answer this question. I think I have made this work thanks to zomg from #zftalk.
Here is my new Twitter Model:
<?php
//application/models/Twitter.php
class Twitter_Model_Twitter
{
private $_twitter;
private $_username;
private $_password;
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
$this->_twitter = new Zend_Service_Twitter($this->_username, $this->_password);
} else {
$twitterAuth = new Zend_Session_Namespace('Twitter_Auth');
$config = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV);
$key = $config->encryption->salt;
$iv_size = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$password = mcrypt_decrypt(MCRYPT_XTEA, $key, $twitterAuth->password, MCRYPT_MODE_ECB, $iv);
$username = $twitterAuth->username;
$this->_twitter = new Zend_Service_Twitter($username, $password);
}
}
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$pieces = explode('_', $key);
foreach($pieces AS $piece_key => $piece_value) {
$pieces[$piece_key] = ucfirst($piece_value);
}
$name = implode('',$pieces);
$method = 'set' . $name;
//$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
//I added this method. So that I could "inject"/set the $_twitter obj
public function setTwitter($obj)
{
$this->_twitter = $obj;
return $this;
}
public function verifyCredentials()
{
return $this->_twitter->account->verifyCredentials();
}
public function friendsTimeline($params)
{
return $this->_twitter->status->friendsTimeline($params);
}
//in the real code, more will go here...
}
And in my Unit Test, I have this:
<?php
// tests/application/models/TwitterTest.php
require_once ('../application/models/Twitter.php');
class Model_TwitterTest extends ControllerTestCase
{
public function testVerifyCredentials()
{
$stub = $this->getMock('Zend_Service_Twitter', array('verifyCredentials'),array(),'',FALSE);
//FALSE is actually the 5th parameter to flag getMock not to call the main class. See Docs for this.
//Now that I have set the $_twitter variable to use the mock, it will not call the main class - Zend_Rest_Client (i think)
$stub->expects($this->once())
->method('verifyCredentials');
$model = new Twitter_Model_Twitter();
//this is the part where i set the $_twitter variable in my model to use the $stub
$model->setOptions(array('twitter'=>$stub));
$model->verifyCredentials();
}
}
Anyways, I think I got it working.
1) The unit test no longer tried to connect to twitter.com:80
2) After I got the setOptions() working in the Twitter_Model, $model->verifyCredentials() in my unit test was successfully called.
I will wait for others in Stackoverflow to confirm that is the right answer. For the meantime, would like to hear from you guys.
Thanks!!!