Generate unique usernames in rails - ruby-on-rails

How can I generate unique usernames based on a format. So for example the first user signs up. The username for him is AAA001 the second user signs up the username will be AAA002, and it keeps incrementing like that. How can I set it up so even if two users click sign up at the same time the database just sends them each a unique username. This is gonna be done on RoR.
Thank you in advance

you can generate the username in the after_safe hook of the model and save! again in a transaction (to prevent race conditions) and use the DB-ID of the already saved user to generate the username

You need to use database transactions.
Here is an example:
ActiveRecord::Base.transaction do
#code goes here
end
This will ensure that the code inside the transaction is blocking.

Related

Attaching existing order (order.email) to create an account, Devise?

This is the scenario I would like to accomplish:
User creates an order (enters email into order table).
User is sent order confirmation email with link to sign up.
If User decides to sign up, it will connect to the account.
What would it take to accomplish this?
I'm a bit mixed up because the Order is already created and wouldn't have a current_user for the order model to attach to the User model. How would I then have it so the order.buyer_id (which is used for the current_user of the User who creates the Order) then gets associated with the to-be-signed up User who just created the order. How could I somehow embed this information in the "sign up" link that gets sent? To somehow say "if this email signs up and is confirmed, become the buyer_id for said order"?
Also, is this good practice?
Other option:
Or should I just have a "email" and "password" field when checking out where the User field get sent ahead of the payment token so the order attaches to the current_user?
Does anyone have other ideas or what I should do?
Other than this, the only thing I'm currently doing is a simple "sign up before ordering if you want to save your orders"
Email is unique for each user so after user signed up and account created you can run a small query where you assign created user to orders. It can be accomplished in the model
class User < ActiveRecord::Base
after_create :connect_orders
...
def connect_orders
Order.where(email: self.email).updated_all(buyer_id: self.id)
end
end

Rails adding row to DB through admin approval

I am making an application in Rails and I have Devise about to be included for users management. I have an index page where all the database rows are in with CRUD ability which is all good.
The functionality I'm after is that if a user wants to add a new row, they can go through and 'add' it but before it gets actually added, the request goes through to an admin, then the admin can hit 'approve' and then the row gets added.
The issue is me trying to get around the fact that the program would 'hold' an action of adding a row and the admin would approve it, then the program would let that action apply.
Thanks.
Instead of going through the hassle of preventing saving the row to the DB (you'd have to cancel the saving in a before_create callback but keep the data somewhere anyway and pass them to admin for approval somehow), I'd do a much simpler approach.
Add another column to the table, boolean column named e.g. approved and by default show only all approved rows in the listing. When a user creates a row, save it with approved = false and let admins access these unapproved rows and approve them simply by setting approved = true or delete them if approval is denied.
Also consider creating a DB index for this column for performance reasons.

Pass Account ID to New User During Sign Up

Currenntly, my application is designed using Devise for authentication. I have it so when the first user signs up, an account is created in an Accounts table and the account_id is passed to the User table. I also have it set so that each time a new account is created that user is tagged as an admin. Finally, I have it working where the admin can create new users.
My problem is that at the time the new users are created I need to have these users be assigned the same account_id as the admin to tie the users together. I can do this if I add an account_id field on the form and have the admin manually enter it. What I want to have is that this is automated in the background.
I tried many varieties without success. This is one of the unsuccesful attempts where I put the following in the user.rb
before_save :add_account_id_from_parent
def add_account_id_from_parent
return true unless self.users.present?
self.users.update_attribute(:account_id, 1)
end
I used the number "1" just to see if I could get anything automated and placed in that field.
Like I said manually everything works, but I want it so the acocunt_id is automatically added during sign up based on the admins account_id.
I'm a bit confused why you are calling self.users. If I understand correctly, you want to assign account_id to 1 after a new user is created (as a test). You can do that like this:
before_save :add_account_id_from_parent
def add_account_id_from_parent
self.account_id = 1
end
You don't need to actually update the record since this is assigned before save, and save will write the new value to the db.
Again I might be missing something, if so please clarify.
UPDATE:
If you're validating that account is present, you'll need to change the callback to a before_validation instead of before_save, like so:
before_validation :add_account_id_from_parent

Migrating existing user model to Devise

I'm trying to migrate a legacy app to Rails 3 and change the authentication to use Devise. I've created the model and migrations and imported all the user data.
I don't plan to migrate the passwords over as the existing scheme is not one we'd like to use going forward, but I want to be able to present users with a simple experience.
Ideally I'd like to catch a login error and then check the password with the legacy field and then update the Devise password with it if it matches.
I can see that Warden gives me a callback that can trap errors so I expect I can trap a login error.
However because all the passwords (in Devise) are blank I get errors relating to the hash as the encrypted_password fields are empty.
Is there a way I can update all the user accounts with a random password?
I've seen in Devise::Models::DatabaseAuthenticatable that there is a method 'password=' but if I call that, e.g. in rails console for the app:
User.find(1).password=('new')
=> "new"
I just get the same plain text string back ('new') and saving the user record post this doesn't populate the encrypted_password field.
I've searched around but can't seem to be able to find it. Any suggestions much appreciated!
Ok just in case anyone else is as cloth headed as I have been the last 24 hours, here's how you set the password:
user = User.find(id)
user.password = 'new-password'
user.save
Simple really :)

Generate temporary URL to reset password

I am looking to implement a Forgot Password feature on my website. I like the option where an email containing a temporary one-time use URL that expires after some time is sent to the user.
I have looked at the following pages to get these ideas but I am not sure how to implement this using ASP.NET and C#. As one of the users indicated, if I can implement this without storing this information inside the database, that will be ideal. Please advise.
Password reset by emailing temporary passwords
Thanks.
Probably the easiest way is going to be to modify your users table to add 2 extra columns, OR if you don't want to modify the existing table you could add a new dependent table called "UserPasswordReset" or something like that. The columns are like this:
PasswordResetToken UNIQUEIDENTIFIER,
PasswordResetExpiration DATETIME
If you go with the additional table route, you could do also add the UserID column, make it a primary key and a foriegn key reference back to your users table. A UNIQUE constraint would also be recommended. Then you simply use a Guid in your asp.net application as the token.
The flow could be something like this:
User requests password reset for their account
You insert a new record in the table (or update their user record) by setting the PasswordResetExpiration to a date in the future (DateTime.Now.AddDays(1)), and set the token to Guid.NewGuid()
Email the user a link to your ResetPassword.aspx page with the guid in the query string (http://www.yoursite.com/ResetPassword.aspx?token=Guid-here)
Use the ResetPassword.aspx page to validate the token and expiration fields. (I.E. Make sure DateTime.Now < PasswordResetExpiration)
Provide a simple form that allows the user to reset this password.
I know you wanted to avoid modifying the database, but it really is probably the simplest method.
#Alex
You can also use System.Security.Cryptography classes in .NET for the hash algorithms. For example:
using System.Security.Cryptography;
...
var hash = SHA256CryptoServiceProvider.Create().ComputeHash(myTokenToHash);
...
Here, the System.Guid class in your friend, as it will generate a unique (well, unique enough) 128-bit number:
Generate a new Guid ( System.Guid.NewGuid() )
Store that Guid somewhere (Application object maybe?)
Send a custom URL in an email with that Guid
When the user hits the site, make them enter the password you sent in the email
If the passwords match, go ahead and force them to enter a new password
I used a Hashing Class to create unique automatic logins made up of the current date/time and the users email address:
string strNow = DateTime.Now.ToString();
string strHash = strNow + strEmail;
strHash = Hash.GetHash(strHash, Hash.HashType.SHA1);
get the Hash Class from: http://www.developerfusion.com/code/4601/create-hashes-md5-sha1-sha256-sha384-sha512/
Then just take it from the URL using:
if (Request.QueryString["hash"] != null)
{
//extract Hash from the URL
string strHash = Request.QueryString["hash"];
}
I would definitely include the database in this process. Once a reset is requested, it's a good idea to indicate that the account is locked out.
For example, if you are changing your pw because you think your account may have been compromised, you definitely don't want it to remain accessible while you go about the change process.
Also, inclusion of "real" information in the reset token could be decoded if someone really wants it and has the horsepower. It would be safer to generate a random string, save it in the db in the row for that user, and then key back to it when the link is clicked.
This gives you two things:
1) There's nothing to decrypt, and therefore nothing of value can be gained from it.
2) The presence of the token in the user record indicates that reset is in progress and the account should be treated as locked out.
The goal of sending some data|string to user email is validation of account owner. Please care about some points:
Avoid sending important information in reset or activate link.
It's best way to store unique string data conjunction with user
account and send it as that link. but be aware if you send just one
section as link to user email and just check it in page, your
application may be in dangerous by brute-force or dictionary
attacker. It's enough to check a list of string to find some links
and change password. I know that has a little chance, but not zero.
Result:
I think it's better if you
combine user email with string link then encrypt them
(not hash because hashed value can't be reverse) and send to user
email.
User click and your page get the encrypted value.
decrypt value.
extract user email.
find email in database.
compare string from received link with other one attached to user
email in database.
Good luck.
I'd use a hash code to validate details in the password reset url. This can all be done without writing anything to the DB or sending any privileged info to an attaker.
To briefly explain normal password salt and hashing; say the salt is 1111 and the pasword is password, you'd concatenate the two and hash the string 1111password, say this gives you a hash of 9999, you'd then store the original salt 1111 and hash 9999 in your user record.
When you are validating a password you use the stored salt, concatenate the password attempt, hash it and compare with the stored hash. For example asecret becomes 1111asecret but hashes to 8888. This doesn't match the original hash so the password match fails.
Of course the salt and hash would normally be properly generated and calculated with established crypto libraries (don't invent your own!).
For the password reset URL I'd put in the unique identifier for the user, i.e. email address, the date the request is made, and a new hash. This hash would be generated from those details concatenated together plus the salt and hash already stored for the user.
For example:
Email: user#example.com
Request Date: 2014-07-17
Salt: 1111
Hash: 9999
Generate a new hash of those concatenated, i.e. 'user#example.com2014-07-1711119999', say this gives a hash of 7777.
The URL I then generate would then have the email, request date and the new hash:
https:\\www.example.com\ResetPassword?email=user#example.com&requestdate=2014-07-17&hash=7777
The server will combine the email and supplied date with it's salt and hash and confirm the hash it generated is the same as the supplied one. If this is Ok then it will show the reset form with the same three parameters hidden behind it, otherwise an error. These get resubmitted and rechecked when the new password is entered to prevent that form being spoofed.
The email address needs to be supplied to make the request and it is only sent out in an email to the same address. the date is hardly priveleged info and the hash is not reversible so gives nothing anyway. Nothing has been written to the database and any tampering with the parameters causes the hash to fail and the URL to report an error.
There is an issues with this approach. A safe hash makes the token really long. Either you integrate the salt into the hash itself (makes it about 20 charactes longer), or you store this unique salt in the database. If you store the salt in the database, you could as well store a random token which is not derrived from any existing
Depending on your needs, you could encrypt information, in a format similar to the following format
(UserId)-(ExpireDate)
Encrypt the data, make that the link, then decrypt the data and take action from there...
Crude, but most likely usable, and not requiring DB usage

Resources