I've implemented basic RBAC functionality in my application using Yii. There are roles, tasks and operations set up and working fine.
What I'm trying to accomplish is the following: on user login, check if the user is privileged to log in to this certain area of my application. There's a operation assigned to this particular task. The problem, however, is that the preferred method of checking is the user is permitted to use a operation is CWebUser::checkAccess, which isn't available in the login as the Yii login workflow goes like this:
The user provides information needed for authentication.
An identity instance is created with the user-provided information.
Call
IUserIdentity::authenticate to check if the identity is valid.
If
valid, call CWebUser::login to login the user, and Redirect the user
browser to returnUrl.
If not valid, retrieve the error code or
message from the identity instance and display it.
taken from here
The CWebUser instance available globally via Yii::app()->user is not propagated while in the login state, (list items two and three) which makes perfect sense to me. However, in order to call checkAccess I'd need a CWebUser instance. CAuthManager doesn't seem to provide the functionality I need as it is (there's no method for checking if a user has a certain atomic permission (operation) or list all of them).
In my example, the operation needed is mostly always assigned in conjunction with a role, but who knows - somebody might just have access to this one operation of logging in, which is a case I want to cover.
thanks a lot!
CAuthManager has already a checkAccess method, which performs what you need. For more information you can check Yii's official documents:
IAuthManager
Related
First off, I am new to Symfony, so I don't know the exact functionality of bundles like FOSUserBundle etc., as well as the terminology.
I am looking for a way to use the Symfony security component in a way that enables me to decouple a user's login credentials (say, a login name and a password, maybe the roles) from the actual user meta data (like first name, last name, groups, birth date, etc.).
The idea is that a user can login with different sets of login name/password and still be recognized as the same user (in the end that means, getting the same User object).
To me, this comes especially handy in combination with multiple authentication providers. For example, if I wanted to enable my users to login via an OAuth2 service as an alternative to logging in with their locally-hosted login name/password-combination (like StackExchange does, for instance), how would I do that?
I have read multiple pages of documentation of Symfony, e.g.:
https://symfony.com/doc/current/security.html
https://symfony.com/doc/current/security/entity_provider.html#create-your-user-entity
This latter one even says at 1) Create your User Entity, that a password is a field of User.
I have recently created just that, first I named my classes UserCredentials and UserDitails and because of the confusion of the getUser function i decided to rename UserCredentials to User. I agree with Alex Blex about the relation (manyToOne on the User side). When you decouple your login and metadata you will need to learn how to embed forms or creating your own form models, which is something I am currently working on and you might need to implement callback functions for deleting and updating entities. The more I work on my UserBundle, the more it looks like the FOSUserBundle. There is quite a bit of work of designing it to work well but it's great for learning. If you wish to inspect how the FOSUserBundle works, after you install it, you can find it in the vendor/friendsOfSymfony folder, it might provide a few answers.
For the last two days I read through multiple resources on authentication in Symfony2. I think I understand most parts, however I feel that I do not understand the whole thing. I successfully created two authentication providers and a UserProvider but I don't understand how to customize the login process.
What I try to do is writing a REST API that will be consumed by a client that I also write. The client will display a form and sent the credentials via Ajax. Therefore, I don't want to use HTTP basic auth (I don't want a browser window to pop up), I don't want to use OAuth or any token (but use credentials) and I don't want to use a form_login (the login form is rendered by Angular).
Using my custom authentication provider, I can verify that a client is logged in, using a cookie I set in the login controller. However, I don't understand how I would login (or what logged in means in Symfony) a user if I'd try to use the session/Symfony security bundle. I could just avoid all security things from Symfony and roll my own implementation but I would prefer to understand how Symfony expects the authentication provider and the login controller to work together.
What I want is one route where a client can check if it is logged in, login and logout.
GET /session # return the session if it exists, or 401
POST /session { session: { email: testy#mctest, password: test1234 }} # login
DELETE /session # destroy session
How would I achieve this in a "Symfony" way?
(Bonuspoints for explaining the general authentication concept in Symfony, outside of the authentication provider)
I'll try to make it the more verbose and simplest that I can from what I personally know :-).
This is what I understood from when I implemented my own Security layer based on the Symfony Security component. If something is not right, please tell me and I will correct it to make it more accurate.
The Security component works with firewalls. A firewall defined a "zone" that is under the responsibility of the Security Component.
For each firewall, you would usually match a pattern that would be matched with the current request. If it matches, then the Security Component tries to determine if the request is allowed to access the resource.
In order to do this, the Security component is divided into two steps :
First : authenticate the user. If the user is not authenticated, we cannot authorize him. So the component will go through its Authentication Manager in order to authenticate the user. Authenticating a user in the Symfony way means trying to create a Token (which is actually juste an instance of the TokenInterface class) matching the current request that is going to be authenticated, and then try to authenticate it.
This Token is supposed to be created thanks to Authentication Listener(s) (classes implementing the ListenerInterface interface). For instance, one will usually use a UserProvider to set a User into the Token object.
Then, when the token is created, it doesn't mean that the Token is authenticated. This Token will be authenticated thanks to the Authentication Manager. An often use kind of Authentication Managers is based on providers that will check if the credentials (or something else) is wrong with the current token. For example, a DaoAuthenticationProvider will check if the password given with the token user matches with the one in what our users provider gives to us. If not, it fails : either another provider can authenticate the current token, or this will be an authentication fail.
http://symfony.com/doc/current/components/security/firewall.html
Second : authorizing the user. For this, I suggest you to read the chapter about it in the Symfony documentation online. You can find it here. It is based on an AccessDecisionManager that will decide if the current user, based on the authenticated token, is now allowed to access the resource. This is usually made thanks to classes called Voters that will vote in order to decide if a user is allowed to access the current resource.
A voter could vote no, another could vote yes, and another could vote I don't know. The final decision is made from the AccessDecisionManager that will, based on how it is configured, determine if these votes will either allow the user ("Any yes allows the user" or forbid him ("Any no is strict").
I think that this is the basis of what you should understand, to me, about the Security component. Keep in mind that it can be very tricky to understand first, especially for the Authentication part.
Last but not least, I highly recommend you to read this whole chapter : Here !. It is the key and your bible if you want to understand what's happening inside this tricky but awesome component.
Now, for your login issue : what would be useful for you is to check how to create your custom Listener (see the authenticating part of my answer) in order to create your own business logic of "how do I authenticate my user based on the current request". Your form would be an entry point of a firewall zone that would be then pointing to this firewall zone. This listener would check if the information provided by your form is inside the request, and then create the Token. Then, you would have a custom way to authenticate your token thanks to the information you provide.
(Sorry for my english !)
I'm building an app that allows the app to hit an ACT url which then triggers a module method to create a new entry using the ExpressionEngine API. However, as there is no user logged in / loggin in, it is not allowed to submit an entry to a channel.
What is the best way to do this. Bypass the EE api and submit the entry manually, or log a user in progamatically..but then how would that work with sessions etc etc?
If the answer is to "log a user in" it would be great to see a code sample if possible.
Thanks!
As you mention, there are 2 ways to add a new entry:
manually add the database records
use the Channel Entries API (http://expressionengine.com/user_guide/development/api/api_channel_entries.html)
The main differences are that entries added using the API will:
perform all the usual data validation (ie, fields that are marked as being required must not be empty)
run any 3rd-party extensions that installed
keep the site statistics up to date (eg, the number of posts by the author)
Adding the entry manually is reasonably easy for simple channels, but gets more complicated if you are using 3rd party fieldtypes that use additional tables.
To log a member in you should just need to:
// Get the member's id (the member must have permissions to post entries)
$member_id = 1;
// Create session
$this->EE->session->create_new_session($member_id);
use the channel entries api to add the entry, and then:
// Log user back out
$this->EE->session->destroy();
when you have finished.
You are going to want to take a look at the Auth.php class in ./system/libraries/Auth.php. This class is an abstraction of the authentication API's and allows you to do exactly what you want. The "loggin as user" feature uses these same methods, as well as the member module.
You can also take a look at the Authenticate module (which is free) so you get an idea of how others work with the Auth class.
https://objectivehtml.com/authenticate
Here is some pseudo code for you to use:
// Fetch the member from the DB using whatever logic you need
$this->EE->db->where('username', 'some-username');
$member = $this->EE->db->get('members');
$this->EE->load->library('auth');
// Load a new Auth_result object which logs in the member
$authed = new Auth_result($member->row());
$authed->start_session($cp_session = FALSE);
$member->free_result();
This code should work, but I didn't not have time to execute it, which is why it's considered pseudo code.
I should also add that I am not sure if this even the best way to solve your problem. I am simply answering the questions of "how to programmatically login a user without knowing their password and without submitting a login form."
I have an auth resource in my library that is correctly returning one of the various statuses based on whether or not the user exists, wrong password, or correct password. I'm trying to get the identity into my controllers though. So in the auth resource I return the status along with user. What needs to happen in my controllers then? I've already handled logging in and redirecting to a different controller. I'm trying to do something like $this->auth->authenticate() and $this->auth->getIdentity() to make sure that they are already authenticated and that they have an identity. What do I need to do so that the identity gets passed around to all of the controllers that I call auth in predispatch?
Also, is there something in Zend Auth that keeps track of how many attempts at logging in have been made?
If use stored the indentiy with Zend_Auth you can get it everywhere you want with:
Zend_Auth::getInstance()->getIdentity()
see also Matthews Tutorial on Zend_Auth
Accessing the Zend_Auth singleton will work, but if you want to get the exact values returned by your resource, put something like this in your controller:
$auth = $this->getInvokeArg('bootstrap')->auth;
I am trying to create a login system thats generic so that it can be adapted for use in various apps. I decided that 2 main "parts" of the system will be User Meta Data & Roles/Resources/ACL.
1. Metadata
I thought of keeping most data like what meta data are available for users in the database, so that admins can manage them using some GUI.
Problem is how can I then configue how I want inputs to render (textbox, checkbox, radios etc.). Then another problem is validation, filters.
2. ACL
I think for simple ACL it will work fine. But suppose I want say users to be able to modify posts they own. In Zend_ACL that is accomplished with Assertions. I thought that will make a "simple" login system overlly complex? Also it will be hard to build I suppose?
Currently I have my database like
Logging in users: I recommend using a separate controller (call it Auth for instance) that has loginAction and logoutAction. Zend_Auth (Zend_Auth using database) will check the database for the right credentials. Once the user is verified, you will save it in the global accessible place(the Zend_Auth class has methods to do this). This is also a good moment to query which roles the user has and store them.
Metadata part of your application: I'm not sure what the question is exactly but I assume you want to store dynamic information about user and have a GUI for admins to manage this. Why you would render different types of controls? Validating the information can be done by defining a lot of the most common metadata (like Twitter) and create rules for them. In the save action for the metadata you would validate using these rules.
ACL: Resources rarely change, you are better off putting them in a configuration file (for speed). You should give a lot thought to resources: what are they exactly to you? Controllers? Modules? Create a plugin that will preDispatch every request checking the role of the logged in user against the requested resource. E.g.:
$action = $request->getActionName();
$controller = $request->getControllerName();
// role, resource, privilage
if (!$acl->isAllowed($user->role, $controller, $action) {
//go to access denied page!
}
Now that Zend_ACL is used for the global access rules, you are better off checking for specific access inside the action itself (like if ($loggedInUser == $article->author) {//edit the article};).
Also don't forget Zend_ACL can be integrated with Zend_Navigation to hide menu items users are not allowed to use (and more).