How to organize user credential in DB? - mysql

What would be the best approach to storing user credentials in MySQL database.
I mean structure, not the data (hash vs clean text).
I have site where we got
Root Admin (1)
Client Admin (1)
Client Sub-Admins (N)
Employer Admin (N)
Employer Sub-Admins (N)
Above users login at back end page
Users (N)
Users login at front end of the site
N - means unlimited
Guess my question is should i put all admins in same table? However employers are not so trusted as Client Admins.
If i put admins in different tables, how i do authentication in PHP? Take data from both tables and merge into array then look in array if user exists?

If you're only ever going to have a single root/client admin I would suggest simply storing their user_id values separately, possibly in a configuration file that is difficult to modify by something like SQL injection. Otherwise the following scheme should allow you a fair amount of flexibility to assign as many users to as many groups as you want.
TABLE users
user_id INT PK AUTO_INCREMENT
user_name VARCHAR
user_password VARCHAR
...
TABLE groups
group_id INT PK AUTO_INCREMENT
group_name VARCHAR
[possible permissions declarations,
or create a similar group_perms table]
...
TABLE user_group_map
user_id INT PK
group_id INT PK

One way to do it is to separate the authentication/user profile data from its possible array of roles. So, assign roles to your users. Hell, assign contexts to your roles. The way I see it, you have a 1-to-many relation between user and roles, in which the roles are assigned a context (Admin of Employers, Admin of Clients, General Admin...).

I would not use multiple tables just to separate users based on how much I trust them. After reading your question and comments in your case you would have to create individual tables for root admins, client admins, employer admins and regular users since your trust level is different for each group. Implementation / maintenance can become really ugly when you start coding even with only two tables...
You seem to be very determined to go with separate tables though and if you choose to do so there are techniques you can "hide" this, for example, create a view to merge these tables, use SQL union in your DAO, or read each table and merge the data programmatically, etc. Again, as you build your application you will most likely have issues with this design.
I would rather use a single table for users and introduce the concept of roles with a many-to-many relation between them. Upon user creation I would associate each user with a default role (eg. user root-admin has a role of root-admin, etc.) along with any other roles that they seem to fit (eg. user cleint-admin has an additional role of client-sub-admin, etc.) Based on the requirements there could be roles that do not relate to users directly, eg. guest, member, etc.
I would also introduce the concept of application contexts with an optional many-to-many relation to roles then initially associate the role root-admin with the application context all meaning that s/he has no restrictions. Additional contexts would be created based on business requirements (eg. role employer-admin is part of the context hire-employees, etc.)
When authorizing, a security manager component can grant / deny access based on the user and its associated roles / contexts with additional logic included if necessary (eg. user is disabled).
Your next concern was that you have additional data for certain users which does not apply to others. In order to resolve this I would introduce the profiles table with an optional one-to-one relation to the users table (not every user has a profile, eg. user root-admin does not).
Schema (draft):
__________________ __________________
|USERS | |ROLES |
|==================| |==================|
|id | |id |
|username | 1..* 1..* |name |
|password | -------------- |description |
|failedattempts | |... |
|disabled | | |
|... | | |
|__________________| |__________________|
| 1 | 0..*
| |
| |
| |
| 0..1 | 0..*
__________________ __________________
|PROFILES | |CONTEXTS |
|==================| |==================|
|id | |id |
|firstname | |name |
|lastname | |description |
|email | |... |
|dob | | |
|... | | |
|__________________| |__________________|

I've described a user and group permission system in this post (ignore the title -- the original question was poorly worded):
Applying column permissions for a table over a trigger

Related

How to extract relational data from a flat table using SQL?

I have a single flat table containing a list of people which records their participation in different groups and their activities over time. The table contains following columns:
- name (first/last)
- e-mail
- secondary e-mail
- group
- event date
+ some other data in a series of columns, relevant to a specific event (meeting, workshop).
I want to extract distinct people from that into a separate table, so that further down the road it could be used for their profiles giving them a list of what they attended and relevant info. In other words, I would like to have a list of people (profiles) and then link that to a list of groups they are in and then a list of events per group they participated in.
Obviously, same people appear a number of times:
| Full name | email | secondary email | group | date |
| John Smith | jsmith#someplace.com | | AcOP | 2010-02-12 |
| John Smith | jsmith#gmail.com | jsmith#somplace.com | AcOP | 2010-03-14 |
| John Smith | jsmith#gmail.com | | CbDP | 2010-03-18 |
| John Smith | jsmith#someplace.com | | BDz | 2010-04-02 |
Of course, I would like to roll it into one record for John Smith with both e-mails in the resulting People table. I can't rule out that there might be more records for same person with other e-mails than those two - I can live with that. To make it more complex ideally I would like to derive a list of groups, creating a Groups table (possibly with further details on the groups) and then a list of meetings/activities for each group. By linking that I would then have clean relational model.
Now, the question: is there a way to perform such a transformation of data in SQL? Or do I need to write a procedure (program) that would traverse the database and do it?
The database is in MySQL, though I can also use MS Access (it was given to me in that format).
There is no tool that does this automatically. You will have to write a couple queries (unless you want to write a DTS package or something proprietary). Here's a typical approach:
Write two select statements for the two tables you wish to create-- one for users and one for groups. You may need to use DISTINCT or GROUP BY to ensure you only get one row when the source table contains duplicates.
Run the two select statements and inspect them for problems. For example, it's possible some users show up with two different email addresses, or some users have the same name and were combined incorrectly. These will need to be cleaned up in order to proceed. There is great way to do this-- it's more or less a manual process requiring expert knowledge of the data.
Write CREATE TABLE scripts based on the two SELECT statements so that you can store the results somewhere.
Use INSERT FROM or SELECT INTO to populate the tables from your two SELECT statements.

Database issue - how do I set up user accounts/pswds so they can ONLY add/change THEIR data?

Okay... I am working to create a mobile app that allows two groups of users to do two different things.
Essentially, the goal of the project is this:
Group A users: create account/pswd and can enter THEIR data into the database and/or change THEIR existing data (but ONLY their data)
Group B users: can SEARCH the database for information that is inserted by Group A. Down the track I'd like to set it up so that they can create an user account so they can also SAVE key information to THEIR account for faster recall (so they don't have to look up the info they search for regularly) -- but that is down the track.
I have a relational database set up using the mySQL that is available with my web-hosting account (it seemed to be the easiest way to go).
I'm just trying to work out how to handle the user account creation/authentication bit, because each group should ONLY be able to CHANGE/INSERT data to their own account, but can search for information submitted by anyone else.
Thanks in advance.
Use mysql facilites to manage permissions: roles, users and privileges.
Navigate through mysql official documentation (i.e. http://dev.mysql.com/doc/workbench/en/wb-adding-roles.html).
You can create two roles: groupA that can INSERT/SELECT/UPDATE one set of tables, groupB that can do the same but in another set of tables.
You can assign INSERT privilege in just the table you want, but SELECT privileges on all the tables.
Hope this info brings you some light...
Firstly this sounds like a huge project, I am sure there are frameworks out there that can do this for you. However, if you are trying to do this on your own continue reading.
This can be done several ways. I will try to be as detailed as possible. This requires SQL as well as application development/Software engineering knowledge.
Step 1: Setup your database
You will need the following tables: All ids are primary keys auto incremented, the other fields can be varchar, except fields that have date in their name
sessions [id, uid, random_token, datecreated]
resourcescope [rid, name]
user [uid, first, last, email, username, salted_pwd]
user_type [id, name, description]
user_resourcescope [id, uid, rid] //lookup table between userid and resourcescope
I prefer using Java or python because you can use dependency injection or decorators. As a result, you don't have to write a lot of code when checking if a user has access.
Putting it all into practice.
1. When a user signs up, you save them into a user database. Depending on the user type, you give them different permissions. Next, you save the user permissions inside the user_resourcescope table.
You should now have the following.
User Table
UID | first | last | email | username | salted_pwd | usertype
1 | james | iri | example#isp.com | jiri1928 | klasdjf8$kljs | 1
UserType table
usetype_id | Name
1 | Basic users
2 | Searcher
ResourceScope Table
rid | Name
1 | FindContent
2 | CreateContent
3 | DeleteContent
User_Resourcescope
id | uid | rid
1 | 1 | 1
2 | 1 | 3
Session
id | uid | random_token | datecreated
1 | 1 | ldkjfald882u3u | 1391274870322
Each resource represents a request within the system. For example,
http://api.myapi.com/content/add - This would be associated with the ResourceScope CreateContent
http://api.myapi.com/content/delete- This would be associated with the ResourceScope CreateDelete
http://api.myapi.com/content/search - This would be associated with the ResourceScope SearchContent
When someone tries to create content, you check if their cred are correct by validating their session information and you check to see if they have the correct permission by checking the User_Resourcescope table.
To prevent users from deleting content that is not theirs. Inside the content table you can add a creator field and put the user id associated with the content. And if someone try to delete content you can check their user id against the creator field.

SQL Server table with dynamic columns?

I am wondering how to approach this problem.
We have a profile table in our database that will be populated by a process that reads
an uploaded excel document and then dumps the columns and the data in their rows into the
user's profile (the only thing common between all such excel spreadsheets is an email address).
Accordingly, we can't really predict what a given user's profile is going to look like.
How do I create my profile table(s)?
Sorry I have to create another answer, but comment wont let me create the ascii sketch
----------
| user |
----------
| id pk|
| name |
| ..... |
----------
----------------
| preference |
----------------
| user_id fk | <-- reference user.id
| header |
| value |
----------------
csv_row=1,churk,height,11,weight,500lb,width,22,...
OR csv_row=1,churk,height=11,wieght=500lb,width=22......
this will yield 1 row in user table, user.id = 1, user.name = Churk
at least 3 rows in preference. {[1,height,11],[1,weight,500lb],[1,width,22]}
So when you query the DB, all you need is
SELECT * FROM user JOIN preference on preference.user_id = user.id WHERE user.name = 'Churk';
If you have someway to process these data using a programming language, and not blinding doing mappings, the it should be pretty simple.
User_table with an ID, and some fix info such as name and what not.
Then you have a profile table that has a user_id foreign key, and key pair value, header / value
How will this data be queried later? As much as I hate the practice, this may be a case of needing to store csv data in a column.
Update:
This might be a good fit for an Entity Attribute Value schema. I'm not really a fan of EAV either, but at least it's less evil than csv data in a column.

the same field in multiple tables

I have the following tables: trainers, trainees and health professionals.
I need to incorporate the mailing list field into each of them to flag those people who want to receive our newsletter.
Is it ok to have the same filed in each of these tables, or there is a better way to resolve this issue?
Many thanks, Zan
You should probably have one single people table, which contains mailing_list. Then if you have different data that needs to be stored for trainers, trainees etc, hold this in separate tables and make them joinable through the use of a foreign key.
I would try to avoid duplicate the data as much as possible,
for the mailing list, here how I would do to avoid having to repeat it in different tables with
Foreign keys
| trainers | | trainees | | Health_prof |
|_________________| |_________________| |_________________|
| .... | | .... | | .... |
| mailing_list_id | | mailing_list_id | | mailing_list_id |
and a table mailing list
| mailing_list |
|_________________|
| mailing_list_id |
| all orther infos|
in the case your persons can be registered to multiple mailing list I would use a third table to make the link between the people and the mailing list with the corresponding ID's as rows, so to register, unregister you would just have to insert/delete rows in this table
Assuming that there is common information (such a name, gender, date of birth, email address, etc) for trainers, trainees and health professionals, you would want to store that information in a separate table, say person_info.
If you plan to have only 1 newsletter or very few types of newsletters (e.g. site updates, offers, etc), then you could store that info as one combined or one-per newsletter type in the person_info table.
But in my experience, the newsletter system should be its own set of tables in which the subscriptions are stores as rows, more like EAV rather than as columns. This reduces the amount of DDL needed when a new newsletter type is required by the business folks.

Whether to merge avatar and profile tables?

I have two tables:
Avatars:
Id | UserId | Name | Size
-----------------------------------------------
1 | 2 | 124.png | Large
2 | 2 | 124_thumb.png | Thumb
Profiles:
Id | UserId | Location | Website
-----------------------------------------------
1 | 2 | Dallas, Tx | www.example.com
These tables could be merged into something like:
User Meta:
Id | UserId | MetaKey | MetaValue
-----------------------------------------------
1 | 2 | location | Dallas, Tx
2 | 2 | website | www.example.com
3 | 2 | avatar_lrg | 124.png
4 | 2 | avatar_thmb | 124_thumb.png
This to me could be a cleaner, more flexible setup (at least at first glance). For instance, if I need to allow a "user status message", I can do so without touching the database.
However, the user's avatars will be pulled far more than their profile information.
So I guess my real questions are:
What king of performance hit would this produce?
Is merging these tables just a really bad idea?
This is almost always a bad idea. What you are doing is a form of the Entity Attribute Value model. This model is sometimes necessary when a system needs a flexible attribute system to allow the addition of attributes (and values) in production.
This type of model is essentially built on metadata in lieu of real relational data. This can lead to referential integrity issues, orphan data, and poor performance (depending on the amount of data in question).
As a general matter, if your attributes are known up front, you want to define them as real data (i.e. actual columns with actual types) as opposed to string-based metadata.
In this case, it looks like users may have one large avatar and one small avatar, so why not make those columns on the user table?
We have a similar type of table at work that probably started with good intentions, but is now quite the headache to deal with. This is because it now has 100s of different "MetaKeys", and there is no good documentation about what is allowed and what each does. You basically have to look at how each is used in the code and figure it out from there. Thus, figure out how you will document this for future developers before you go down that route.
Also, to retrieve all the information about each user it is no longer a 1-row query, but an n-row query (where n is the number of fields on the user). Also, once you have that data, you have to post-process each of those based on your meta-key to get the details about your user (which usually turns out to be more of a development effort because you have to do a bunch of String comparisons). Next, many databases only allow a certain number of rows to be returned from a query, and thus the number of users you can retrieve at once is divided by n. Last, ordering users based on information stored this way will be much more complicated and expensive.
In general, I would say that you should make any fields that have specialized functionality or require ordering to be columns in your table. Since they will require a development effort anyway, you might as well add them as an extra column when you implement them. I would say your avatar pics fall into this category, because you'll probably have one of each, and will always want to display the large one in certain places and the small one in others. However, if you wanted to allow users to make their own fields, this would be a good way to do this, though I would make it another table that can be joined to from the user table. Below are the tables I'd suggest. I assume that "Status" and "Favorite Color" are custom fields entered by user 2:
User:
| Id | Name |Location | Website | avatarLarge | avatarSmall
----------------------------------------------------------------------
| 2 | iPityDaFu |Dallas, Tx | www.example.com | 124.png | 124_thumb.png
UserMeta:
Id | UserId | MetaKey | MetaValue
-----------------------------------------------
1 | 2 | Status | Hungry
2 | 2 | Favorite Color | Blue
I'd stick with the original layout. Here are the downsides of replacing your existing table structure with a big table of key-value pairs that jump out at me:
Inefficient storage - since the data stored in the metavalue column is mixed, the column must be declared with the worst-case data type, even if all you would need to hold is a boolean for some keys.
Inefficient searching - should you ever need to do a lookup from the value in the future, the mishmash of data will make indexing a nightmare.
Inefficient reading - reading a single user record now means doing an index scan for multiple rows, instead of pulling a single row.
Inefficient writing - writing out a single user record is now a multi-row process.
Contention - having mixed your user data and avatar data together, you've forced threads that only one care about one or the other to operate on the same table, increasing your risk of running into locking problems.
Lack of enforcement - your data constraints have now moved into the business layer. The database can no longer ensure that all users have all the attributes they should, or that those attributes are of the right type, etc.