I am creating a web app for a business which includes several departments. I am using PHP and MySql to do this, in my database i am storing the privileges as ENUM with options N (No access), R (Read Only) and RW (Read and Write).
Once a user logs in, a php script finds the appropriate privileges and stores those in a session. For example: if a user has RW for Production, then there is a session variable $_SESSION['production'] = RW. Now whenever i need to see if a user is allowed to edit production then i check with the session variable and take appropriate actions.
I have 20 such categories of privileges. This certainly isn't the way store something like this. This will simply increase the load time and hog plenty of memory. Are there any alternatives to store the user rights?
Note: The 20 category list is expected to grow.
I typically use
$_SESSION['privileges']=array(
'someprivilege' => 'r',
'someotherprivilege' => 'rw'
)
to keep the memory requirements in context: 100 privileges, the name consisting of 20 chars, the value of 5 chars, will add up to less than a page (4k). The load time issue is heavily mitgated by PHP using a very efficient bnary serializer on session files.
Why wouldn't you store access rights in database and check them when you need by user id?
You'll need to store only user id in $_SESSION.
This simplifies the whole application.
If you store access rights in session, you need to be always sure they they are always up to date at any single point of time. For example if admin wants to change user access rights, they need to have additional ability to log user out. OR for example you need to update your $_SESSION everytime user tries to access the page, which is overhead and anyway requires checking database every time.
You can create an identity class that encapsulate all your users informations (id, privileges and have in it getters for those properties) and have it instanciated once the user is logged in by getting privileges from DB by using user's id.
then each time you need to find his rights just call getRights(); from your identity class.
You should only pick the permissions when they apply. This will keep your load limited to what is needed.
Alternatively you could store the permissions per user in a serialized form. Then this would be a single retrieve operation (instead of 20 or more). Encapsulate the logic behind that so you can equally easy store this both inside the database and session, and easily manage and store them.
Apart from these general suggestions, I can not see much "load" with your question anyway. You need to do what you need to do. You could reduce the number of permissions if you want less hassle, but that will obviously not be useful as you want that.
And then, what's your problem with sessions? Or more specifically, what do you mean by load?
Related
On my website, users can currently compare multiple company for a prestation.
For each company, I calculate the price of the prestation.
To calculate this price I have a very big SQL request in order to filter companies based on the user's previous input and get every parameter I need.
Once the query end, I loop througt the company list and calculate the price for each of them and for each additional services the company offer. Then I display those value to the user in HTML.
The user can then add or remove an option and order the company.
Then, when a user choose a price, I send the company_id along with the user's otpion (the different services he chose) to the server and get the price previously calculated from the user SESSION.
Prices are stored in user session in order to avoid the calculation process but, I have like ~6 prices by company and usually ~30 companies for one user request. Which mean that I store in session an array with around 180 different prices for one user.
I found it quite wastefull to store this many variables in session and I was wondering, is there a better way to store those variables ? Should I store them in database ?
By the way, server side, i'm using PHP along with Mysql for the database.
What you're effectively doing is a very primitive form of caching. Sadly, the session is not the best place to do so, for a variety of reasons:
The session can never be shared between users. Some values cached may be the same for every user. It's good to have a cache that allows you to go the "unique" or "shared" routes at will.
Is your session cached data used on every page? If it is, then forget this point. If, however, it isn't, on every page, you're still incurring the cost of fetching (which, depending on your server configuration, may involve a few fs calls, or network calls, or a combination) and deserializing the data, on every request. This, if your session payload is large, can make a significant different to load times.
Another point to consider is the simple fact that, if you are running a straight-out-of-the-box LAMP stack and have not configured a shared session driver, you're going to find a very nasty surprise when you scale out :-)
Before we go any further, ask yourself these questions:
Do the values in the session change on a user-by-user basis? And if they do, is it by a fixed amount of percentage?
Do the values in the session change often?
How are the values calculated?
If #1 is "No"
You are better off caching one copy and use it for every user.
If #2 is "No"
You are better off denormalizing (i.e. pre-calculating) the values in the database
If #3 is a complicated formula
See #2
In every case
MySQL is a very poor cache driver, and should be avoided if you can. Most people replace it with redis, but that's also not a very good way due to its inability to scale by itself (you need nutcracker/twemproxy to make it properly shard). Either way, if your data to be cached is relatively small, MySQL will do. However, plan for the future.
If you do plan on going the proper cache way, consider a key-value store such as Riak, or a document storage driver such as Aerospike.
I am currently working with a medium-sized team developing a custom content management system for a large client. The CMS is written using PHP and follows the MVC pattern (custom). It is a modular system, for which plugins can be added to the system by us or other developers at a later stage.
The system will contain user-based permissions, and a series of generic roles that have predefined permissions. It is required that a super-admin user can also modify permissions on a user basis (for example John Doe might be defined as a regular user, but has the possibility of modifying content).
Opinion is currently divided about the best way for us to store and handle these permissions. Half of the dev team are suggesting to add a new DB table that will store key/value pairs and user IDs for each user, with boolean values stored in each record. The table structure would be something like this:
user_ID: the ID of the user
perm_name: the name of the permission
perm_value: a boolean value dictating whether the user can carry out this action
The proposal is that if the value associated with a particular permission is set to 0, or does not exist in the table, the user does not have the required permission.
The other half of the dev team is favouring storing the permissions in a single field as a JSON-encoded string within the users table. So for example, we would store the following JSON for John Doe):
{
'modifyProducts': 1,
'addProducts': 1,
'addPages': 0
}
We would then be able to use json_decode() within the User class to extract the permissions, for example:
$this->permissions = json_decode($dbval);
I am personally leaning towards the latter option for two main reasons:
It is scalable
It does not require us to modify the database if we need a new permissions.
In short, what is the best approach for such an application?
I think the best solution in this case would be to use NoSQL database, such as MongoDB - this way you can still keep the scalability and take advantage of the JSON structure.
On the other hand, depending on your user table you could take possible advantage of column type indexing and optimize your requests for querying and reading, if of course you're working with normalized database.
I personally would store JSON within a relational DB only when I want to directly display the info and not use it for any querying. Just like you've said yourself - there's always the possibility of ending up with huge and growing JSON string and this would most probably cause troubles at some point.
I was using mysql to check if the user was logged in or not, but I received an email from my hosting company saying that I had exceeded the mysql queries limit and the cost to upgrade the mysql queries limit to 150000 per hour was $240/year.
Now, I need an alternative way to check if the user's logged in or not, like writing the user's timestamp in a file, but I don't know the best way to do it... And I don't even know which tags I put on this post...
So, could you please help me with this problem?
Use $_SESSION. It's 1000 times more efficient and designed for just this purpose. Read about it here
Upgrade (or from cost perspective downgrade) to a root-server and/or use better mechanisms to store sessions. ie memcache, couchdb or mongodb.
In every case, you have to handle semaphores by hand, if you leave PHP's session handler.
It is rather odd that your host has a mysql query limit, but back to your question;
I will take a guess that you are not just trying to see if a user is logged in locally, but rather, having another account see that the user is logged in, which would require a database call, not a $_SESSION (which is maintained on a per-user basis).
If, however, we make the assumption that you want to do this outside of the database, you can generate the end HTML result when a user logs in or out. Keep a table in the database with the status of the users, but whenever a user logs in or out, make ONE query to that table, and generate, say, your HTML list of logged in users. Save the output to a file, and when you need to render a page, include that file instead of making a database call.
It's not completely removing the MySQL database, but it is reasonably reliable, and should significantly reduce your number of database queries.
Assuming I have a valid session and an authenticated user, what are some ways to go about implementing user authorization in an application with a PHP/MySQL backend, and a heavy JavaScript front-end?
Most of the implementation examples I can find seem too focused on user authentication and the authorization just sort of happens. For instance, an if statement checking if the type of user an admin. This seems way too implemented to me.
In an implementation like mine, there is no way of knowing what "page" the user was on when they initiated the request. So, a method of only serving certain content for certain users, determined by PHP, is too broad for what I need to do.
Ideally each entity has a sort of access control list based either on the user explicitly or what group or type the user is/in.
I went to a local bookstore and spent an afternoon looking through all they had on PHP, MySQL and JavaScript. Surprisingly, most of the books had virtually nothing on user authorization. That scares the hell out of me! This has to be solved by anyone building a large web application that uses AJAX, I just can't seem to find something to get me started.
I would appreciate any and all feedback, experiences, tips, etc. (Any books on this subject?)
PHP security seems stuck in the dark ages of single password gives a token for a single user for a class of particular pages. You seem to be wanting to get a lot more fine-grained in your app, maybe even allowing access to specific pieces of resources depending on that login token. Your thought of access control lists is absolutely correct, and yes, you've discovered the dark secret: no one really published how to design or write an ACL mechanism. That said, it has been done.
First, are you familiar with unix file permissions? The're the -rwxr-xr-x things you see in an ls -l on the command line. Unix has chosen a very simplified approach to ACLs. Each person logged in has a User ID (UID) and one or more Group IDs (GID) (whoami, groups). The Unix file permissions allow three operations, Read, Write, and Execute which can be on or off. With 2^^9 states, these permissions easily fit in an integer, and Unix can then attach that
integer to the file directly in the file system. When a user attempts to access a file, permissions are compared from strict to permissive, matching the most permissive privileges allowed. So, users get the first set of permissions, groups get the second, and anyone gets the third. Thus, an executable is usually 755: only the owner can change it, but anyone can read and use it.
Second, LDAP is the Lightweight Directory Access Protocol, a system designed to give multiple network users access to resources. OpenLDAP is a common Linux implementation, and Microsoft's Active Directory on Windows Server speaks LDAP (with a lot of extensions). LDAP has a much more robust system of ACLs. A general configuration is access to [resources] by [who] [type of access granted] [control] or access to dn="uid=matt,ou=Users,dc=example,dc=com" by * none to limit all access to to Matt's user information. For a much more complete discussion, I would highly recommend Mastering LDAP, specifically chapter 4 on security. (This is where I get a bit out of my direct knowledge.) I am under the impression that LDAP stores this information in a separate database table, but I don't know that and can't find documentation one way or another. I am keeping an eye out for a possible schema for that.
Short stop to summarize: ACLs take a concept of a user token with possible groups above the user level, a collection of objects to secure in some way, and several consistent possible operations on those pieces- 3 dimensions of information. Unix stores two of those dimensions with the thing to be secured directly. OpenLDAP stores those three dimensions separately, in some way we don't quite know, but that I suspect is a linked tree structure.
Given that, let's take a look at how we could design an ACL system for a RESTful web application. For assumptions, we will break your application into discrete addressable units- each thing that needs to be secured will be accessible via a URI (http://example.com/users, http://example.com/page_pieces/ticker). Our users will be a simple UID/GIDs token- a user can be part of a several groups. Finally, our available operations will be based on the HTTP requests- GET, POST, PUT, DELETE, etc. We now need a system that efficiently handles a 3-dimensional array of data. Our schema should be pretty obvious: (uri, userid, groupid, operations). We deliberately denormalize the operations column into a string list of GET,POST,... so we only need one table. There is no primary key, since we will never really be looking up by ID.
Queries will be done in two steps: SELECT * FROM acl WHERE uri=#uri, userid=#userid which will return 0 or 1 rows. If it returns 1 row, we're done and can grep permisssion to see if the operation is in the list (use * to indicate all perms). If we got 0 rows, run a second query SELECT * FROM acl WHERE uri=#uri, userid='*', groupid in (#groupid) which will again return 0 or some rows. If it returns some, loop through and look at perms. If it returns 0, do one last query SELECT * FROM acl WHERE uri=#uri, userid='*', groupid='*' which will finally return 0 or 1 row. If it returns 1, look at perms. If it returns 0, take the default action.
We can set permissions in several ways:
INSERT INTO acl VALUES (#uri, #userid, '', 'GET,POST') allows a single user GET or POST access
INSERT INTO acl VALUES (#uri, '*', 'admin,contributors', 'GET,PUT,POST,DELETE')
INSERT INTO acl VALUES (#uri, '*', '*', '') denies all access.
A couple things to note:
All URIs must be expressed exactly; this solution has no way to set
default permissions at a higher level and have them trickle down
(left as exercise to the Questioner).
Uniqueness of uri/uid/gid pairs should happen at some point. The app can handle it, or in MySQL you can do ALTER TABLE acl ADD UNIQUE INDEX (uri, userid, groupid) (look up documentation for similar constraints in other DBMSes).
It seems that you are looking for something called Access Control List aka ACL (which is dead according to Zed Shaw, great video).
It's pretty hard to give a you a solution without knowing what kind of backend you have, but you might check out how other are doing that.
For something specific to the lithium framework (PHP), see: Lithium Access Control
This is what I understand:
You need to build an access control list for your users? do you?
[correct me if I'm wrong]
I suggest you to create a DB table in which you can store the User ID (or username) and what kind of access it has on your Web Application. Then you can check the table to know if the requested URL/resource is accessible to that user. That's all.
Designing a fairly complicated site with a lot of ajax running on a single page. I have reached the point where some user's need to have specific permission to do things and some need to be stopped from the action. I have set up user roles in my database and all is working fine, but I wonder if there is an easier/safer method for me to store each permission.
Currently, when a user logs in their specific permissions are grabbed from the db and loaded into a session array. To check if the user has permission, I simply check to see if the permission is contained in the array. This seems sluggish, and almost like I am missing a better solution.
Also, sessions can apparently be edited by the user... is there a safer method?
I have thought running a query for each check, but that could greatly increase the load time for a simple ajax request.
I am open to any and all ideas. Thanks.
First and foremost, the user cannot edit Session variables. The only thing that is saved on the user's machine is a Session ID. That ID is then used by the server to grab key/value pairs that are stored ONLY on the server. From a client's standpoint, it is impossible to change values on a whim.
Second, I would not worry too heavily on a database connection. Avoid repeating yourself, but don't worry too much about the first connection.
Finally, my favorite way to do multiple permissions without creating roles is to use binary math. Some people like this, some people don't, but I find it useful.
To use this method, imaging that we define the following values:
CAN_EDIT_SOMETHING = 1 // Powers of 2
CAN_SEE_SOMETHING_ELSE = 2
CAN_DO_ADMIN_STUFF = 4
... = 8
To give people multiple permissions, use binary OR
PERMISSIONS = CAN_EDIT_SOMETHING | CAN_DO_ADMIN_STUFF
To illustrate how this works, we can look at the bits:
0b0001
OR 0b0100
---------
0b0101
To check if someone has a permission, use binary AND
if( PERMISSIONS & CAN_EDIT_SOMETHING != 0 ) {
}
To see how this works, we look at the bits again
0b0101
AND 0b0001
----------
0b0001 // Not equal to 0. They must have that permission!
The final benefit of this method is that it allows you to combine multiple permissions easily into "meta-permissions"
// If both EDIT_SOMETHING and ADMIN_STUFF are tasks that an admin
// can perform, we can combine them easily
//
IS_FULL_ADMIN = CAN_EDIT_SOMETHING | CAN_DO_ADMIN_STUFF
// We can then use this value exactly as we do any other permission
//
PERMISSIONS = IS_FULL_ADMIN | CAN_SEE_SOMETHING ELSE
Use it if you want, but it is a nice trick to have in your arsenal.
Seems OK to me! You could look at some software to enhance your session chache peformance.
Querying the DB every time is not as bad as it sounds! Firstly you probably need to connect to the DB anyway, secondly if you queried the users permisions when they signed in then the chances are that all the relevent rows are sitting in the buffer and no IO is required, thirdly a query for a single permision for a single user is going to be a lot lighter than a query for all permisions for a user.
Your explanation of the model seems a bit confused. Permission is the product of the subject authorization and the object authorization. Do you really store these products for every combination of subject and object? That's a very inefficient solution and very hard to manage.
Also, sessions can apparently be edited by the user
WTF?????!!!!
Session data should only ever be changed by the methods you define in your code - if users are able to modify any part of the session data in any way they like then this is the first problem you need to address - until you do, it will be virtually impossible to rely on any part of your authentication/authorization method unless you move authentication completely out of the domain of your application code (BTW: this is not the right way to fix the problem).
Certainly searching a very large array (not sure of the actual breakpoint - but in the region of n=1000 - but there are lots of variables affecting this) can be significantly slower than fetching the results from a database.
Its hard to say what you're doing wrong without understanding how your current system works. Is it one of these?