i have created a controller in which the default index action is being used to display a login form and then authenticate the user. However i have ended up having to add functions into the controller which i feel will clutter up the controller.
for example i have functions like:
protected function _process($values)
{
// Get our authentication adapter and check credentials
$adapter = $this->_getAuthAdapter();
$adapter->setIdentity($values['username']);
$adapter->setCredential($values['password']);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
$user = $adapter->getResultRowObject();
$auth->getStorage()->write($user);
return true;
}
return false;
}
protected function _getAuthAdapter() {
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password')
->setCredentialTreatment('SHA1(CONCAT(?,salt))');
return $authAdapter;
}
What would you recommend to do, maybe create another directory called custom_classes and include the file into my controller this way?
Given your example I would put this into some sort of ACL centered Zend_Controller_Plugin class and register this class in your bootstrap to ensure it is always run.
Also, depending on what you are doing, the logic could go into one of your Models.
Models should represent a collection of information that make up an entity of some sort, the Model class should also be responsible for reading, updating, removing and adding new Models.
For example a User Model could represent a User in a table in your database. It might contain functions like updateFailedLogins(), updateLogins() and specific functions related to the log in process for that particular User Model.
If the methods you add are necessary for the controller to handle the input it receives from the User Interface, then it's fine to have them there. If it's a different concern, identify which responsibility it is and add the method there.
If you find the logic in the methods is useful in many controllers, consider making the logic into a Zend_Controller_Action_Helper. If you find this is that has to run on every request but is not directly related to a Controller Action, make it into a Zend_Controller_Plugin.
In case of authenticating users, you might want to create a Zend_Controller_Plugin that authenticates the user before the actual Controller Action is called.
Related
I'm implementing a LOG of the events that occurs on the system. In other words, detect when an object is created, and save the data that was created.
When an update comes, I need to store the previous object state and the new one. As I'm using API resources with custom data implementations, I'm reusing them to gather all the needed information.
public function update(Request $request, Attendant $attendant)
{
$attendantData = $request->input('attendant');
$prevResource = new AttendantResource($attendant);
$attendant = AttendantService::Update($attendant, $attendantData);
$resource = new AttendantResource($attendant);
$this->createLog($prevResource, $resource);
return $resource;
}
In the previous code, create a new resource before the Attendant is modified. But at the end, $prevResource and $resource have the same data. The information from $prevResource is update toO.
Is there a way to clone the Model? or instead, Is there a way to prevent the reference update from the $prevResource?
Use laravel's model observers(https://laravel.com/docs/5.8/eloquent#observers) to observe events on models and log what you need to.
You can observe created, creating, updated, updating, deleted, deleting, saved, saving events.
In my opinion this is not the way to handle this type of problem (in the controller).
What you actually want to do is Register an event listener that does the logging when your eloquent model is updated. There are also model "observers".
There is a library that already handles the specifics of how to make all of this work together that at very least can act as an example of how to set everything up, but is already capable of doing what you want.
Here is the specific documentation on "Logging Model Events."
The nice thing about using the Spatie Logger is that it's been manifested as a simple trait you add to your model. Here's some example code from the documentation:
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;
class NewsItem extends Model
{
use LogsActivity;
protected static $logAttributes = ['*'];
protected static $logAttributesToIgnore = ['text'];
protected static $logOnlyDirty = true;
}
This illustrates a few different customizations including the use of $logOnlyDirty that will only log changed items, vs the default of providing a complete before/after of the entity. You can also ignore certain attributes you don't care about (timestamps or calculated fields for example.
My application has 3 different scenarios:
Controller action is accessible for authorized users only
Controller action is accessible for non-authorized users only
Controller action is accessible for any user, but auth check is done
First two scenarios are handled by simple filters, and since all scenarios are logically similar, it seems that the remaining scenario should be processed by filter too.
So, what I would like to do:
When user accesses some specific controller action (i.e. HomeController#getIndex), the filter should check whether the user is authorized or no, set controller's $user property to Auth::user() value and share this value between all the views. Here's the code I came up with:
Router::filter('auth', function() {
$route = Str::parseCallback(Route::currentRouteAction(), null); // 0 - controller name, 1 - controller action name
$controller = App::make($route[0]);
$user = Auth::user();
$controller->user = $user;
View::share('user', $user);
return $controller->{$route[1]}();
});
This code works, however the controller is invoked twice (i.e. when controller action has no return construct), and auth filter invokes controller before any other 'before' filters are called.
Second solution:
Create AuthController, and use it's constructor to set $user property, however the constructor will be called for every child controller action (I would like to run it on specific actions only, so I think filters are the way to go).
Third solution:
Create UserRepository and code a method, that will do all the auth stuff, and call it from actions that need auth (not setting $user property). This solutions seems to be an overkill for a few lines of code.
Fourth solution:
Pass Auth::user() value to controller action directly (as a param), but I haven't found any convenient way to do so yet.
Is there any kind of "best practice" solution for handling auth in described way?
Is there any other ways of solving this problem?
Best practice would probably be to create a UserRepository class and service providers for each controller that would inject the user repository, but like you said, probably overkill for something simple.
Maybe an easier solution is to add a function to BaseController.php which sets the user.
public function setUser()
{
$this->user = Auth::user();
}
And then whenever you need to set the user, just call $controller->setUser();. If you always need the controller to do this, just call $this->setUser() in the constructor so it's always available.
Thanks to #user3158900 I did some research and came up with another solution.
Since placing expose method inside User model is kinda unintuitive, it seemed right to extend authorization facade.
This is my final solution:
Create a Guard class in app/extensions/Auth/ directory. This class extends Laravel's native Illuminate\Auth\Guard class.
<?php namespace Extensions\Auth;
class Guard extends \Illuminate\Auth\Guard {
public function exposeUser() {
$user = \Auth::user();
\View::share('user', $user);
return $user;
}
}
Create an extensions.php file in app directory and include this file into app/global/start.php.
require app_path().'/filters.php';
require app_path().'/extensions.php';
New extensions.php file contains following code (this code adds new auth driver):
Auth::extend('eloquent+', function() {
return new \Extensions\Auth\Guard(
new \Illuminate\Auth\EloquentUserProvider(App::make('hash'), \Config::get('auth.model')),
App::make('session.store')
);
});
Set auth config's (app/config/auth.php) $model property to 'eloquent+' (this makes our auth use 'eloquent+' as auth driver).
Modify composer.json to reflect new autoload class map.
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"app/extensions"
]
},
Finally run composer dump-autoload.
Now you can use your Auth::user() and other auth methods as usually, plus we can share model between views and access it from any action simply calling Auth::exposeUser() in our controller actions.
It would also be nice to get some feedback about this approach.
I'm going for an example to make the question easier:
If you are implementing facebook, you want to separate user information from account information, from posts, from from from, that means there will be a model for user, a model for user info, a model for posts, a model for for for.
now if you go to user profile, it loads information from all these models, the question is how would you structure the controllers, of course you need controllers for each model to define it's unique actions, but how do i handle the rendering?
do i make a new controller that takes care of all renderings? or do i just put render profile in the user controller - which means it will have access to other models - or do i put in each controller a rendering of it's own and then combine them together...
or maybe if you have a better approach?
i just do not know the consequences of each approach that i thought of, and i don't know if there is a better approach that i didn't think of.
As others mentioned, you don't need a controller specifically for each model. However, given your Facebook example, it's quite simply done through relationships.
An oversimplification would be:
A User model to hold the user's account information and relationships
class User extends Eloquent {
public function posts() {
return $this->hasMany('posts', 'from_user_id');
}
}
A Post model to hold the content and from/to relationships to User
class Post extends Eloquent {
public function from() {
return $this->belongsTo('user', 'from_user_id');
}
public function to() {
return $this->belongsTo('user', 'to_user_id');
}
}
Then perhaps you'd have UserController load a view like so:
class UserController extends BaseController {
public function index($id) {
$user = User::find($id);
return View::make('user_page', array(
'user' => $user,
'posts' => $user->posts()->with('to')->get()
));
}
}
of course you need controllers for each model to define it's unique actions
No, this is wrong.
Controllers are part of your presentation layer, some might say you need a controller for each view object which is okay but for the model layer, it has nothing to do with the number of controllers you have.
a View is an object that toggles multiple templates and once again this has nothing to do with your controller, rendering is a view responsibility.
to understand it more, take a look here , here and here
those are really useful information to start with.
Wrong answer - controllers are not part of presentation layer - they are part of transport layer. Their task is to react on HTTP request.
Request is dispatched by Router. To proof take a look at Laravel source. Controller is a part of Routing package. When you call for example:
Route::get("/","HomeController");
You are just registering request parameters in RouteCollection. But everything is happening inside Router. Controllers can exist without any kind of view layer.
I am developing a part of a web app build on Symfony 2. Like in many apps, authentication & authorization is required. How can I continue developing, taking considerations ACL by passing or faking a login?
In the docs, login_check does the authentication and sessions part transparently. I think I may either need to implement a version of that or somehow call it to login as different users/roles
I'm not entirely sure I understand your question but are you just asking how you log in programatically?
In my tests I simply do a call like:
$this->container->get('security.context')->setToken(
new UsernamePasswordToken(
'maintenance', null, 'main', array('ROLE_FIXTURE_LOADER')
)
);
In this instance 'maintenance' is not even a real User entity, its just a username that I made up for my fixtures so that they can access a service by having ROLE_FIXTURE_LOADER but if you did want to login as a full user entity (so that they have the correct ACL ID) you can get a $user object from the database and call:
$this->container->get('security.context')->setToken(
new UsernamePasswordToken(
$user, null, 'main', $user->getRoles())
)
);
This doesn't do a full login but it does work with RBAC and I don't see why it wouldn't work with ACL if you pass an actual user object.
As for functional testing of my front end if I need to login I just navigate to the login page and submit the form as per the testing docs. For either of those to work you do need access to the container so you need to extend WebTestCase or roll your own ability to boot the kernel (see here).
I've a feeling I've mis-understood the question though (i.e. you need to do something more complex than just placing the token). Perhaps you could try to clarify a bit more what you mean by
passing or faking a login
A concrete example to plant a security token in a test:
First we make a base class for our tests to use which contain a login convenience method. This can be done by extending WebTestCase and using the getContainer method on a client OR you can pull WebTestCase apart to roll your own base class which just boots the kernel without a client and returns the container (see my link for two solutions to achieve that).
namespace Acme\SomeBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
abstract class AcmeTestCase extends WebTestCase {
protected function loginAs($client, $username) {
$container = $client->getContainer();
$doctrine = $container->get('doctrine');
$user = $this->loadUser($doctrine, $username);
// First Parameter is the actual user object.
// Change 'main' to whatever your firewall is called in security.yml
$container->get('security.context')->setToken(
new UsernamePasswordToken(
$user, null, 'main', $user->getRoles()
)
);
}
private function loadUser($doctrine, $username) {
// Don't have to use doctrine if you don't want to, you could use
// a service to load your user since you have access to the
// container.
// Assumes User entity implements UserInterface
return $doctrine
->getRepository('AcmeUserBundle:User')
->findOneByUsername($username);
}
}
Then you just need to use your base class in any test you wish. Like so:
namespace Acme\SomeBundle\Tests\Entity;
use Acme\SomeBundle\Tests\AcmeTestCase;
class SomeEntityTest extends AcmeTestCase {
public function somethingTest() {
$this->loginAs(static::createClient(), 'SomeUsernameInDB');
// Do the rest of your test here.
}
}
Hopefully this helps.
Not sure I understand your question very well but If you just want to see the application with different user role you can use the Symfony Role Switcher documented here :
http://symfony.com/doc/current/book/security.html#impersonating-a-user
So you just have to put a parameter in your url to view your app as another connected user.
Hope it helps !
Check out:
https://github.com/schmittjoh/JMSSecurityExtraBundle/tree/master/Tests/Functional
I am working on building a lightweight MVC, mainly for the learning process but I would like it to be good enough to use eventually.
Below is a basic example/demo of how a basic controller might would look, let's assume the URI has been processed and routed to this controller and these 2 methods.
1) I need to get data from database/cache/etc... inside my Model classes, I just need help on how I should load my models into my example controller below, you can see that I have added this below $profileData = $this->model->getProfile($userId) that is just made up and does not exist's, how could I get something like that to work though? Or should I load the model into the class a different way?
2) A lot of pages will require a user to be logged into the site. SHould I process that part below in the controller to check if a user is logged in, example, before building the profile page, check if user is logged in, if not then build a login page instead and add these checks inside of each controller method/page?
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriendlist();
$this->view->load('userFriends', $friendsData);
}
}
core
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
There are probably tons of ways to accomplish what you are trying.
The "easiest" is probably to just override the constructor and instantiate the model directly.
in User_Controller:
public function __construct(DependencyContainer $dc) {
parent::__construct($dc);
$this->model = new User_Model();
}
I'm guessing that you are looking for something a little more automated though. If you want the Model to have the same name as the controller minus "_Controller", just use get_class($this) in the constructor and use PHP's string functions to parse out what you want. Once you have that in a variable, you can use that variable to instantiate the model:
in Core_Controller:
public function __construct(DependencyContainer $dc) {
$this->view = new Core_View();
// $model_class should be 'User_Model' now
$model_class = str_replace('_Controller', '_Model', get_class($this));
// now instantiate the model
$this->model = new $model_class();
}
I haven't actually worked with any framework that can only have one model associated with each controller (except may CakePHP? I can't remember). With Symfony, the models and controllers are completely decoupled so you can use any model with any controller. You just instantiate the model as need. Symfony use the Doctrine ORM so for example, in a controller action, if you needed a model you would do something like this:
$model = Doctrine::getTable('User');
It might be worthwhile to consider a design more like that in order to promote a decoupled design and I promise that you will want more than one model in some controller at some point.
2.) As far as authentication. Something that seems to be fairly common is to have some sort of setting (whether in a config file or a member variable) that says whether or not the current action needs the user to be authenticated. This is processed each time the action runs (Yii calls these kinds of things filters). If the user needs to be logged in, it stores the page that they are trying to access, and then redirects them to a log in page (you should only ever have to create one). Once they properly authenticate, it will redirect them back to where they were originally heading.