Database Schema for group - mysql

I have a simple user and group database.
User: uid(pk), name, password ( don't worry about password in plain)
Group: gid(pk), name, owner_uid
Member: uid (fk), gid (fk) ( a user can belong to multiple group)
Now, I do not trust my database administrator and want to encrypt/secure specific coloumns so that db admin cannot be able to add any existing uid to any gid. How I could modify my scema and which coloumn I should encrypt.
I have a encryption key which is assumed to stay secure and I wanted to utilize sql queries also so cannot complete encrypt the tables and store as blob.

I think you can not perform this requirement with database constraints. You can manage it by programming. However if you write some triggers to handle it, your (bad) administrator can access triggers too.
Add an extra column to Member like key_info.
In Insert: make a hash code (like sha256Hex) with combination of uid and gid and specific salt. And insert with uid and gid into Member.
In Selects from Member: you have an extra difficulty. You should select the records (from Member) that have correct hash code.
If you have a big and complex salt and your administrator can not access the salt (as you said in comments), he/she can not find the codec algorithm. So if he/she added some records into Member, your codes ignore the added record in selects, because the key_info can not be true.

Related

Should I use multiple databases in MySQL for my "hosting" platform? [duplicate]

Let us say I need to design a database which will host data for multiple companies. Now for security and admin purposes I need to make sure that the data for different companies is properly isolated but I also do not want to start 10 mysql processes for hosting the data for 10 companies on 10 different servers. What are the best ways to do this with the mysql database.
There are several approaches to multi-tenant databases. For discussion, they're usually broken into three categories.
One database per tenant.
Shared database, one schema per
tenant.
Shared database, shared schema. A tenant identifier (tenant key) associates every row with the right tenant.
MSDN has a good article on the pros and cons of each design, and examples of implementations.
Microsoft has apparently taken down the pages I referred to, but they are on on archive.org. Links have been changed to point there.
For reference, this is the original link for the second article
In MySQL I prefer to use a single database for all tenants. I restrict access to the data by using a separate database user for each tenant that only has access to views that only show rows that belong to that tenant.
This can be done by:
Add a tenant_id column to every table
Use a trigger to populate the tenant_id with the current database username on insert
Create a view for each table where tenant_id = current_database_username
Only use the views in your application
Connect to the database using the tenant specific username
I've fully documented this in a blog post:
https://opensource.io/it/mysql-multi-tenant/
The simple way is: for each shared table, add a column says SEGMENT_ID. Assigned proper SEGMENT_ID to each customer. Then create views for each customer base on the SEGMENT_ID, These views will keep data separated from each customers. With this method, information can be shared, make it simple for both operation & development (stored procedure can also be shared) simple.
Assuming you'd run one MySQL database on a single MySQL instance - there are several ways how to distinguish between what's belonging to whom.
Most obvious choice (for me at least) would be creating a composite primary key such as:
CREATE TABLE some_table (
id int unsigned not null auto_increment,
companyId int unsigned not null,
..
..
..,
primary key(id, company_id)
) engine = innodb;
and then distinguishing between companies by changing the companyId part of the primary key.
That way you can have all the data of all the companies in the same table / database and at application level you can control what company is tied to which companyId and determine which data to display for certain company.
If this wasn't what you were looking for - my apologies for misunderstanding your question.
Have you considered creating a different schema for each company?
You should try to define more precisely what you want to achieve, though.
If you want to make sure that an HW failure doesn't compromise data for more than one company, for example, you have to create different instances and run them on different nodes.
If you want to make sure that someone from company A cannot see data that belong to company B you can do that at the application level as per Matthew PK answer, for example
If you want to be sure that someone who manages to compromise the security and run arbitrary SQL against the DB you need something more robust than that, though.
If you want to be able to backup data independently so that you can safely backup Company C on mondays and Company A on sundays and be able to restore just company C then, again, a purely application-based solution won't help.
Given a specific DB User, you could give a user membership to group(s) indicating the companies whose data they are permitted to access.
I presume you're going to have a Companies table, so just create a one-to-many relationship between Companies and MySQLUsers or something similar.
Then, as a condition of all your queries, just match the CompanyID based on the UserID
in my file Generate_multiTanentMysql.php i do all steps with PHP script
https://github.com/ziedtuihri/SaaS_Application
A Solution Design Pattern :
Creating a database user for each tenant
Renaming every table to a different and unique name (e.g. using a prefix ‘someprefix_’)
Adding a text column called ‘id_tenant’ to every table to store the name of the tenant the row belongs to
Creating a trigger for each table to automatically store the current database username to the id_tenant column before inserting a new row
Creating a view for each table with the original table name with all the columns except id_tenant. The view will only return rows where (id_tenant = current_database_username)
Only grant permission to the views (not tables) to each tenant’s database user
Then, the only part of the application that needs to change is the database connection logic. When someone connects to the SaaS, the application would need to:
Connect to the database as that tenant-specific username

Using sAMAccountName as uid in MySQL database

I have an application that authenticate with LDAP and returns a JWT with the sAMAccountname of the logged user.
This application have a MySQL database where I'd like to store the user in different tables (fields like createdBy, updatedBy, etc.) and I was wondering what is the correct way of handling this:
using the sAMAccount name as identifier (so the createdBy will be a VARCHAR(25))
using a link table to match the sAMAccountname with an autoincremented identifier
Normally I would choose the "id" way, it's faster and easier to read in my opinion, but I'm not really into linking users from LDAP dictionary and changing their id in my database, so honestly I would choose the first option.
What are the pro/cons of using a string as uid ? In my case it's likely to be only for statuses like updatedBy, cratedBy, deletedBy etc. so I won't have hardlinks between multiple tables using an user identifier.
I think you should create user table with a surrogate primary key (autoincrementing one) and make unique index on sAMAccount column.
Natural primary keys are good because they just naturally describe a record they point to. But the downsize of using them is that they consume too much space in the index. Index lookups / rebuilds are slower. Tables consume more space also.
I'd connect everything using an id as primary key.
ONe thing is that the sAMAccountName is not necessarilly unique. Think of a user changing her or his name. The sAMAccountName might then change but it's still the same user. When you connect everything via an ID you can change the sAMAccountName-field without breaking everything.
But that's just my 2 cent

Link field values and ID to another table?

How do I link or add the field with all my users called "username" from my main table to another table so I can run checks and compare values without having to add a ton of rows to my main table? This would be a much cleaner and more organized setup.
Table called login_users. This is my main table that stores their basic information
username email password
Anthony user#email.com
Josh user#email.com
Tsuyoshi user#email.com
Table 2 called badge_status. This table will hold their status with whether or not they have unlocked an achievement on my site. I want to bring in the usernames.
username badge1 badge2
Anthony locked unlocked
Josh unlocked locked
Tsuyoshi unlocked locked
Also, could table 2 automatically be updated with a new user when they sign up since all new sign ups are imported into the first table. As a side note, I am using phpmyadmin.
Thanks for any help with getting this set up. Very much appreciated.
I think you should normalize your data by using the following schema instead:
login_users [Table]
username
email
password
badges [Table]
BadgeId int PRIMARY KEY
BadgeName
... (any other relevant columns)
users_badges [Table]
username (foreign key constraint to username column on the login_users table)
BadgeId (foreign key constraint to BadgeId column on the badges table)
This will allow you add as many badges as you want without having to ever change your database schema or scripts.
I don't really understand why you are separating out these tables. In theory, there is a one-to-one relationship between the two. Your query "could table 2 automatically be updated with a new user when they sign up since all new sign ups are imported into the first table" further supports the argument why splitting these tables simply creates unnecessary overhead.
Other sources of overhead include overly complicated JOINs to get the data out and the need to add an additional unique constraint on badge_status.username as well as a foreign key.
If you truly wish to separate out these tables, I would suggest the following setup instead which makes querying for specific badges and adding new badges very easy:
login_users: username, email, password, ...
badges: id, name, description, ...
users_badges: username, badge_id, status, unlocked_date, ...

Storing relations between users with specific viewing permissions

I am designing a database for MySQL to create a relationship between two users of an application. I am unsure of the best way to store the particulars of this relationship, though I currently intend to use one record per relationship in a link table called RELATIONS. Each user has basic data like their name and occupation stored in the USERS table, and more specific personal data stored linked by FKs in other tables, which we'll say are called OTHER1, OTHER2, and OTHER3, which will all contain some other data to be shared, we'll say each in a field called [Data] and identified with ID and USER_ID.
The difficulty is that the application allows users to specify what basic and advanced data that they show to each user, and vice versa. The RELATIONS link table needs to have FKs to USERS for the two users to set up the relationship, but I don't know how best to specify what data each user is able to share, because virtually all of the data that the database stores is stored optionally but all needs to possibly be hidden from a user that doesn't have permission to view it. The second user should be able to see if there is data there, however, so that he might request permission to view it.
My model for RELATIONS at this point looks like this:
RELATIONS
ID
USER_ID1
USER_ID2
USER1OTHER1_ID [(Value), Unshared, Null]
...
USER1OTHER100_ID [(Value), Unshared, Null]
USER2OTHER1_ID [(Value), Unshared, Null]
...
USER2OTHER100_ID [(Value), Unshared, Null]
So USER1OTHER1_ID will contain the FK to OTHER1 if User1 has shared it with User2, will be "Unshared" if it's present but unshared, and Null if User1 has no data in OTHER1. Same for USER2OTHER1 for sharing with User1. I don't like having a massive field array, though, and I don't like how I'd have to update all the relations if User1 later decides to add data to OTHER1. Is there a simpler and more normalised way of representing this?
I believe the normalized approach would be to only store whether userA has permissions to view the userB's data and not add FK references to it in the Relations table because you already have references to userB's data somewhere else. By storing additional references in the Relations table you are duplicating data and will have to ensure that it stays synchronized as you described in your question which will probably be an ongoing maintenance hassle and one more thing you have to keep in mind whenever you refactor your code.
If you only store permissions (no fks) in the Relations table you would join on a table (User?) to get the User's shared data or to see if it exists depending on the permission.
As far as having an excessive number of columns on the relations table, I don't think you will have enough to see a real degradation when querying the table (you could correct me on this). For clarity sake in the db code as well as your application code, I think you are better off to have a column for each permission than to try to find a short cut such as combining them in a clob or something.
The most succinct way I can readily imagine is to store one INT with the relationship, which is a bit-wise representation of the permissions; with the interpretation of that INT in the code. The INT will need as many bits as you have unique permissions, and then define constants for each of them. I'm not sure what language you are implementing in, but there about a few ways to skin this cat...
So, some pseudo-code might look like this:
define RELATION_PERMISSION_SEE_MY_PHOTOS = 1;
define RELATION_PERMISSION_SEE_MY_FRIENDS = 1<<1;
define RELATION_PERMISSION_SEE_MY_EMAIL = 1<<2;
and then build some arrays of supporting info (like localized strings, etc) to build your interface with, and then do something like this to modify it:
int new_permission = 0
foreach(user-selected-permissions as selected_permission) {
new_permission |= selected_permission
}
my_relation_model.permissions_flags = new_permission
one way would be to use essentially key value pairs..
similar to this:
user_1_id
user_2_id
field
privilege
.. because virtually all of the data that the database stores is stored optionally ...
Considering this, I would suggest 6NF for all user attributes.
The User table serves as an anchor and ideally holds only UserID.
Each user attribute has its own table with only UserID and the attribute value (6NF); a row exists only if the attribute is specified (all attribute values are NOT NULL).
Each attribute has also a sharing table with only OwnerID, VisitorID. A row exists only if the owner shares the attribute with the visitor.
In this model, a user can share only attributes that do exists. If you want to allow sharing of not specified attributes, point the OwnerID to the User table too.
To make thing simpler you can (should) create a view(s) for user data.

Can i create each table for each user in my social networking site?

I'm creating a social networking site with features similar to Facebook.
I want to start with schema design for my database.
What i thought was to create each table for each user who registers to our site.. am i doing right?
If a million users register to my site, a million tables will be created. how to go on about optimizing this? Please do suggest me techniques to overcome this and some references or books to learn about such concepts will be vry useful..
Thanks in Advance.
This is not the way you want to do it.
What you want to do is have a table (perhaps called 'users') that contains one row for each user that registers. Creating a new table for each user is completely pointless and would cause terrible performance.
Maybe something like this:
TABLE users
- username AS VARCHAR(255)
- password AS VARCHAR(255) (use a hashed password, of course)
- ...
Then when a user registers, simply insert the information they provide into the users table as a new row.
That would be massive overkill. You should probably read up on database design (start with normalisation, but don't overdo it). Then write down what you want to save for each user, and think about how to save it without saving data double.
But I'm pretty sure a table-per-user is not an option for this.
You must be confusing the meaning of the words database, table, field (or column), record (or row).
A database contains all your data for a specific project. There is always one database per project (or almost always)
A table contains all data of a specific entity and by saying entity, I mean an object type that is imaginable as real or seperatelly existing by itself. A person is an entity, a book is an entity, a phone is an entity, a movie is an entity, etc. Each of these would be seperate tables in a database.
A field (or column) is a data type that represents a specific characteristic (feature) of a table's entity. For example a table of users can have the fields: NAME, SURNAME, AGE, etc. These are all features that a user has.
A record (or row) is an actual item of one table. It is a single 'piece' of the table's entity. For example in a table of users, one record is one single user, namely {NAME:"John", SURNAME:"Smith", AGE:"32"}.
In your example, I can tell you for sure that you only need one database. You want to store information for many users, so you need one table called USER. You will need to store features to your users, like: name, surname, age, address, etc., then you will need to create the respective fields in this table: NAME, SURNAME, AGE, ADDRESS, etc. Then you will need to insert your data in the database as records. It will be one record per user you want to store.