Database Authorised Updates - mysql

How do I design a database structure so that a number of user of a database can add, update and delete records which may include foreign keys from some of the database's tables and prevent these changes from going live until the user's supervisor authorises these changes, some of these changes or declines these changes from occuring. The database will eventualy have a number of these groups of users and supervisors.
What is the best methodology, or system for this, I am using MySQL database at present.

Views.
All access to "live" data should be through views. The views should select only only those rows that have been "authorized". You'll want to manage permissions (SQL GRANT and REVOKE statements) so application code can't bypass the views by going directly to the tables.
This might not be obvious, but that means that all the user interface code needs to go to the views, too. For example, most user interfaces will ease the burden of picking the right foreign key by loading a combo box or list box, popping up a modal dialog, or something along those lines. Those interface elements need to get their data from views of "authorized" rows, too.

Related

Operations between linked tables and native tables

I have three identical tables, one on MySQL, one linked to this one on Access by ODBC, and a native in the same Access database.
When I update the table on MySQL, the linked table on Access updates, and vice versa. But I would like to know if it is possible that the linked table updates the native table (and vice versa)?
Access table
MySQL table
It really depends on how the local Access table is being updated. If it is ALWAYS updated say by a few forms, then you could add a after update even to those few forms, and put in code to update the MySQL table.
Another approch (again you only/always update the local tables) is to add a table trigger to the local table. In this table code event, you can actually have it call some VBA code, and that VBA code could then update/insert to the linked MySQL table. Once again, then the two tables will automatic remain in sync.
The other possible would be to add a time + date stamp column to the tables (both on MySQL side, and on the Access side). You could then write some VBA code to sync up the tables. Such code is not too hard, but in a multi-user setting, this can become quite a challenge, since while you are syncing the data, other users might also update the MySQL tables and thus your sync routines might well miss some tables. Database sync software and this subject can fill a few books the size of medical texts, and is a VERY complex subject.
However, why not just always use linked tables to MySQL, and be done with any requirements to sync data? Access makes a great client to SQL server or MySQL. If you eliminate the local tables, then you eliminate the need to sync your data.

Is it possible to make certain data available to certain users on a public mysql database?

Let's say we have a public DNA database running on mysql. Database contains only complete data. In this scenario, some special users want to add experimental data to the database, which may not be complete or they don't want it to be visible to everyone. Instead they want the experimental data to only be visible to users with correct privileges. What approach would you take to achieve this?
Presumably these datasets are large, and performance is important. That means the privilege system should be as coarse as possible.
If I were doing this, I'd create a "public" database, and use the MySQL GRANT command to allow guest users to SELECT on that database.
For example:
CREATE USER 'guest'#'%' IDENTIFIED BY 'changethispassword';
GRANT SELECT ON public.* TO 'guest'#'%';
Then, for the nonpublic datasets, I'd put them into other databases, and be more selective about the users GRANTed privileges. For example, these GRANTs give two different users access to private information and the public information.
CREATE USER 'venter'#'%' IDENTIFIED BY 'changethispassword';
GRANT SELECT ON public.* TO 'venter'#'%';
GRANT SELECT ON celera.* TO 'venter'#'%';
CREATE USER 'collins'#'%' IDENTIFIED BY 'changethispassword';
GRANT SELECT ON public.* TO 'collins'#'%';
GRANT SELECT ON hgp.* TO 'collins'#'%';
A user who has SELECT privileges on, let us say, the public database and the celera database, can issue queries like this allowing seamless (if not optimally performing) merging of private and public data.
SELECT whatever
FROM public.AGCT
UNION ALL
SELECT whatever
FROM celera.AGCT
Of course, it has to make scientific sense to take the union of these datasets. That may or may not be the case.
Don't be alarmed at the idea of creating multiple databases. They really are nothing more complex than directories in a computer file system. A single server can deliver dozens of them without any problems.
MySQL is definitely up to this kinds of security. Hosting providers run multi-tenant servers routinely.
I would consider MariaDB (a MySQL-compatible database written by MySQL's founder) over MySQL, as it supports roles.
Neither of them support Row Security like Oracle does, but you can mimic it by adding an "owner" column with the name of the role that can select/update the row.
Add a WITH CHECK OPTION view that checks that the current_user is in the role specified in that column.
Add a trigger to set owner value properly.
update: If you can't alter the table but can add new ones, add a new one w same key as original, and add owner column, and join the tables in your view.
See
http://www.sqlmaestro.com/resources/all/row_level_security_mysql/

EJB Timer for deleting database entries

I am currently working on a j2ee web application. The application features a way for users to reset their passwords if they forget them.
I have a database table with 3 columns: username, key, and timestamp.
When the user requests a password change, I add an entry in that table with their username and a random key (making sure that their are no duplicate keys in the table, also that a user can only appear once in the table). I also add the current time. I then send them an e-mail with a link to the application that contains their key, something like:
mysite.com/app/reset?key=abcxyz123
The servlet that handles this request looks at the key in the url to find the matching entry in the reset table to determine which user the key belongs to. If the key doesn't match an entry, I show an error page, if it does, I show the password reset screen. Once the user changes their password, I manually delete the entry from that reset table.
I am trying to implement the equivalent of a time to live for the password reset links, so that I don't have entries loitering in the table unnecessarily, and I thought of 2 options, the first of which I have implemented:
1) Create an EJB Timer that fires every minute that will delete entries in the reset table where the timestamp is older than 30 minutes. This is a manual process in that I am using hibernate as my jpa implementation, so I retrieve all the entries from the table, examine their timestamps, and delete the old ones.
2) Create a database job that deletes rows over a certain age?
My question is, does anyone see any drawbacks to the first approach, and second, is the 2nd option even possible with mysql? I figure that if I can use the 2nd approach, I can get rid of the timer, and let the database handle the time to live aspect of the password reset links, and that may be more efficient.
I haven't been doing j2ee development for that long, but based on the knowledge that I have, these seemed like 2 logical approaches. I welcome any input.
3) Create script that will connect to db, execute delete, disconnect. Then you can schedule this script via operating system e.g. crontab.
Regarding option 1 - Drawback of that solution is that it uses application server resources for stuff that can be done on database only and is not dependent/uses any application logic.
Benefit is that whole app is self contained and you don't need any additional installation/setup task on database as with 2 and 3.

How many db-users should I create?

guys!
I need to ask you a question... I'm mew in programming business and if this question seems silly, please indulge me.
I have a little site where people have to register in order to post something. So I register every user in the database. I log into the db with:
$pdo = new PDO("mysql:host=localhost;dbname="MY-DB", "My-USER", "My-password");
The question is: when I log into the databse to do whatever operation I need (select, update, delete, insert) how many users ("MY-USER") should I create to login to the db ? Should I create one db-user for every users that registers on my site, or one one single user is enough to do the operations ?
Thanks.
In normal situations you should have additionally to a root user, which can create new databases and add new users, you should create one user per application which is accessing the database, so that a bug in one application wouldn't affect another.
In your case this means to have 1 additional user for your web application which has all permissions on one mysql-database and can create the necessary tables as well as read / modify data in those.
You should not use mysql as your user manager, instead create a table with the users, their (hashed) passwords, ... and manage them in your application.

Secure multi-tenancy in MySQL application

I have a JSP/MySQL web service where users interact with "processes" -- they can upload data, configure, view reports, etc for a given process. They can also create new processes or run reports that compare several processes.
Currently, the process id is specified in the URL (a GET parameter) so any user can interact with any process. I have been asked to add security and multi-tenancy to this service. For simplicity, let's say each tenant has full access to a set of processes, but processes may be accessible by multiple tenants.
My preferred approach:
Add a user table (PK_User_Id, password_hash, name, etc)
Add an access table (FK_User_Id, FK_Process_Id)
An SSL login page that stores the Tenant_Id in the Session
A process-select page that lets you choose a Process_Id that you have access to, and stores that in the Session
Almost every page will create its SQL queries based on the Session's Process_Id
"Cross-process" pages like Create, Select, and Compare will work off of the Session's User_Id instead
My boss thinks that this is not secure "enough" to satisfy an external code audit. He fears that a wayward developer could still write a query that exposes one customer's data to another, or something.
He wants me to also use ANSI SQL's built in ROLES (the app must stay DB agnostic) to create a db role for each user. The role will detail which tables the role has access to, which rows in shared tables, etc. This way, upon login, the Connection will be "safe" and no developer mistake can possibly cause issues.
Is this possible?
Are there such a thing as DB-agnostic "Roles" that work with MySQL?
Can the roles specify that you are allowed to add rows to a table iff the primary key is 'foo'?
Is my system "secure enough" by industry standards?
Here is what I do for MySQL multi-tenant with a single database to ensure data is private:
Create a mysql user for each tenant
Add a tenant_id column to each table
Use a trigger to automatically put the current mysql user into the tenant_id column on INSERT
Create a view for each table that only shows rows where tenant_id = mysql_user (do not include the tenant_id column in the view)
Restrict the tenant mysql user to only have access to these views
Since the application is using the tenant's mysql user there is no chance that they can accidentally get another tenant's data.
I was able to convert a large single-tenant mysql application to multi-tenant in a weekend with minimal changes. I documented the design here: https://opensource.io/it/mysql-multi-tenant/
use PostgreSQL instead, as it supports real schemas, unlike MySQL
if you have to use MySQL, do the following:
make one mysql user per tenant
add an indexed column to each table, tenant VARCHAR(16) NOT NULL
add a trigger to each table that sets tenant to the mysql connection username ON BEFORE INSERT
create a view for each table that sets WHERE tenant = mysql connection username. DO NOT include the tenant column in the select list
grant permission to the tenant user for views, but not for tables
And now the user can only see their own tenant information.
We had a similar discussion on multitenancy security and handling requests on so question. But in short I think storing tenantID in session is a huge security risk. User can go from one tenant to other and tenantID will remain the same, also tenantID should not be send through url.