belongs_to with default fallback - ruby-on-rails

Currently, My Rails (3) app creates objects before their relation is known (I store the Comment, then ask the user to log-in/sign-in to validate that comment, after which the Comment gets a User assigned).
I'd like to make sure each Comment falls back to a user at all times. To avoid having to test everywhere for #comment.user. Ensuring a user also allows me to validate_presence.
I can see two ways, both seem quite ugly. Maybe I am missing some feature of Rails or ActiveRecord.
In the seed or migration, create a user with ID 1, call that anonymous and assign that as fallback.
in comment.rb check for user_id on load, if null, assign a User.new to it.
How is this usually done?

How about adding user records for all comments. Even for not signed-in users. This is just one more record per anonymous comment. And in user records you can keep last IP address and other stuff like max anonymous comments per day.
After user sign-in, reassign comments to the right user and delete temporary user. For new user sign-ups update temporary user records to verified state.
Notice that each anonymous comment has owner, but the user isn't verified. And verification state can be treated as user attribute.
Yes, it is possible to keep unverified users data in session. However sessions sometimes are configured to be stored in a database. So result is the same as storing temporary users in main users table in terms of disk usage, but the data is more fragmented.

Maybe temporarily store necessary stuff in session, not in database? I don't like the idea of storing partially complete records in database. It is a risky play with database integrity. Alternatively, you could create a table with text column which would contain those incomplete objects serialized.

Related

How do I implement restricted access to subsets in my database

I can restrict entire controller action access with [Authorize(Roles=...)] however, what about if I want to allow a user to for example look at the data of the other users in their own department (only).
Specifically what I mean is when I give a user the details of another user, the URL will be like this:
myapp.com/user/details/45
And they could quite easily just edit the 45 to be whatever they want. I want to restrict the access if the user the requested is not part of their own company (their company is currently in their session but that can be changed)
I can see a few different ways to do this, but I am just wondering what everyone's preference is based on experience. Here are some options:
1.Change the way access is done by editing the routing. myapp.com/Company/4/User/4' where 4 is a psudoID which references an actual ID internally.
2.myapp.com/OurCompany/User/4' < same but with a controller for ourcompany instead of generically companies.
3. Just check it internally by checking the requested user is in the same company: `myapp.com/User/42345'. My issue is that this has to be customized for each kind of request (users, products, related companies). But this is what I'm looking at right now.
EntityFramework actually has the power to reference the company members of the current user because when they log in I get their company entry and store it in the session. This allows easy access to the members of the company like so:
SessionHandler.UserSession.CompanyTable.UserTable.Where(e=> e.UserID == id).FirstOrDefault();
This seems odd to those who don't know about EF, but basically the subtable UserTable is a subset of all users referenced by the Foreign Key of the CompanyTable, which does indeed return the correct set of entities. The issue with this is that updating the entry in this way actually updates the entry in the SESSION but not inside the DATABASE, which is really bazaar to watch because it means the logged in users can seemingly make changes to the database but infact they are just being saved into the session. (staring at the database whist making these changes confirmed it, logging out and logging in nothing was saved, but db.SaveChanges() was correctly being called, just there were no changes to be made)
First, for the love of everything good and holy, stop putting that in the session. The session shouldn't be used for this, regardless, and it's code smell of the worst kind.
What you're talking about is object-level permissions. The correct way to do that is to limit your queries with some identifying factor of the currently logged in user. Your entities need a concept of "ownership" for this work. In other words, they need a foreign key to some other entity that "owns" that particular instance. Since here you want to restrict by department, that means all your users need to be owned by departments. Then, for situations like this you can do something like:
db.Users.Where(m => m.DepartmentId == user.DepartmentId)
Where user here is the instance of the currently logged in user. By querying in this way, the subset of entities available to the user is restricted by the user, itself. Which means attempting to access a URL with id outside of that set will naturally result in a 404. A 404 is actually the best thing for this, because even though it's authorization based, returning a 403 lets you know something is there - you just can't access it. For a hacker, this kind of information is half the battle. A 404 being returned gives you zero information. It might not exist or it might just not be available to you.

Admin user approving SQL changes from a user

I've read into Cancan and Pundit (also Devise) for managing users in a Rails App. But I wanted to know if something was possible.
Basically, I want to have users change/add lines in a table (using SQLite at the moment, but will be moving to SQL in the future - call them entries). But before it gets added to the actual table, it gets sent to the admin for approval. Then the admin can just hit 'approve' and the statement gets run.
I'm just confused about how to hold the statement and then when approved, the statement runs. Any information would be appreciated.
By statement, do you mean that your users are actually writing the SQL themselves?
If not, I'd setup a second model identical to your first that acts like a queue of some sort that holds the proposed changes/additions. This way, you'll be able to compare the old and new statements if necessary, and, when approved, you'll be able to perform the create/update magic on your original model.
Hope this gives you some ideas!

Changing database structure and migrating password hashes

I currently have a site (Rails 4.1, ActiveRecord, Postgres) where a visitor can log in to one of multiple models — for example, a visitor can create an account or login as a User, Artist, etc. Each of these models have a password_digest column (using bcrypt and has_secure_password).
Now we want to move to the site to a unified login system — everyone creates and logs in as a User, and a User can belong to an Artist and the other models we have.
I think it makes sense to directly use the password_digest column in the User table, rather than looking across all the existing models. This means we'll have to create new entries in the User table and copy the password_digests into them.
Can this be safely done, and would everyone be able to login with the password they already have? I've tried playing around with password_digests in the Rails console (copying digests to known passwords and assigning them to other entries) and it appears to authenticate correctly … are there any downsides to doing this?
There's no uniqueness constraint on passwords (i assume) and so it doesn't matter if the passwords are the same between different User accounts (in the resulting table, with all the Artist etc records copied in). There's no safety issues with copying the data from one table/column to another: there's nothing magical about the password_digest value, it's just a text string. As long as you carry on using the same encryption method then the crypted password you generate to test on login should still match the saved value.
You may have a problem with usernames though, if they are required to be unique: what happens if you have an existing User and an existing Artist who have the same username? Is one of them going to have to change?

How to automatically maintain a table of users that have been authenticated by IIS 7.0 using Windows Authentication

I want to build and maintain a table of users. All users that access the ASP.NET MVC site are authenticated via Windows Authentication so they're bound to have a unique username. I'm grabbing the user name from:
System.Web.HttpContext.Current.User.Identity.Name
I feel like I could go two ways with this.
Anytime the user table or any tables that references the user table are accessed, I could add the user if it doesn't exist. I'm worried this might be very error prone if user's existance isn't checked.
Anytime the user visits any page on the site, check if that user exists in the db and if they don't exist, add the user. This may have a lot of overhead as it'll be checked every page change.
I'd like to hear which of these is the better solution and also how to implement them.
I think a better way would be something similar to the option two.
Anytime a user visits a page, check a session variable to see if that user was checked against the DB. If the session variable is not there, check if that user exists in the DB, add the user to your table if necessary, then set the session variable.
That way you don't have to hit the DB on every request.

Allow users to remove their account

I am developing a gallery which allows users to post photos, comments, vote and do many other tasks.
Now I think that it is correct to allow users to unsubscribe and remove all their data if they want to. However it is difficult to allow such a thing because you run the risk to break your application (e.g. what should I do when a comment has many replies? what should I do with pages that have many revisions by different users?).
Photos can be easily removed, but for other data (i.e. comments, revisions...) I thought that there are three possibilities:
assign it to the admin
assign it to a user called "removed-user"
mantain the current associations (i.e. the user ID) and only rename user's data (e.g. assign a new username such as "removed-user-24" and a non-existent e-mail such as "noreply-removed-user-24#mysite.com"
What are the best practices to follow when we allow users to remove their accounts? How do you implement them (particularly in Rails)?
I've typically solved this type of problem by having an active flag on user, and simply setting active to false when the user is deleted. That way I maintain referential integrity throughout the system even if a user is "deleted". In the business layer I always validate a user is active before allowing them to perform operations. I also filter inactive users when retrieving data.
The usual thing to do is instead of deleting them from a database, add a boolean flag field and have it be true for valid users and false for invalid users. You will have to add code to filter on the flag. You should also remove all relevant data from the user that you can. The primary purpose of this flag is to keep the links intact. It is a variant of the renaming the user's data, but the flag will be easier to check.
Ideally in a system you would not want to "hard delete" data. The best way I know of and that we have implemented in past is "soft delete". Maintain a status column in all your data tables which ideally refers to the fact whether the row is active or not. Any row when created is "Active" by default; however as entries are deleted; they are made inactive.
All select queries which display data on screen filter results for only "active records". This way you get following advantages:
1. Data Recovery is possible.
2. You can have a scheduled task on database level, which can take care of hard deletes of once in a way; if really needed. (Like a SQL procedure or something)
3. You can have an admin screen to be able to decide which accounts, entries etc you'd really want to mark for deletion
4. A temperory disabling of account can also be implemented with same solution.
In prod environments where I have worked on, a hard delete is a strict No-No. Infact audits are maintained for deletes also. But if application is really small; it'd be upto user.
I would still suggest a "virtual delete" or a "soft delete" with periodic cleanup on db level; which will be faster efficient and optimized way of cleaning up.
I generally don't like to delete anything and instead opt to mark records as deleted/unpublished using states (with AASM i.e. acts as state machine).
I prefer states and events to just using flags as you can use events to update attributes and send emails etc. in one foul swoop. Then check states to decide what to do later on.
HTH.
I would recommend putting in a delete date field that contains the date/time the user unsubscribed - not only to the user record, but to all information related to that user. The app should check the field prior to displaying anything. You can then run a hard delete for all records 30 days (your choice of time) after the delete date. This will allow the information not to be shown (you will probably need to update the app in a few places), time to allow the user to re-subscribe (accidental or rethinking) and a scheduled process to delete old data. I would remove ALL information about the member and any related comments about the member or their prior published data (photos, etc.)
I am sure it changing lot since update with Data Protection and GDPR, etc.
the reason I found this page as I was looking for advice because of new Apply policy on account deletion requirements extended https://developer.apple.com/news/?id=i71db0mv
We are using Ruby on Rails right now. Your answers seem a little outdated? or not or still useful right now
I was thinking something like that
create a new table “old_user_table” with old user_id , First name, Second name, email, and booking slug.
It will allow keep all users who did previous booking. And deleted their user ID in the app. We need to keep all records for booking for audit purpose in the last 5 years in the app.
the user setup with this app, the user but never booking, then the user will not transfer to “old_user_table” cos the user never booking.
Does it make sense? something like that?

Resources