Django/Python generate and unique Gift-card-code from UUID - python

I'm using Django and into a my model I'm using UUID v4 as primary key.
I'm using this UUID to generate a Qrcode used for a sort of giftcard.
Now the customer requests to have also a giftcard code using 10 characters to have a possibility to acquire the giftcard using the Qrcode (using the current version based on the UUID) as also the possibility to inter manually the giftcard code (to digit 10 just characters).
Now I need to found a way to generate this gift code. Obviously this code most be unique.
I found this article where the author suggest to use the auto-generaed id (integer id) into the generate code (for example at the end of a random string). I'm not sure for this because I have only 10 characters: for long id basically I will fire some of available characters just to concatenate this unique section.
For example, if my id is 609234 I will have {random-string with length 4} + 609234.
And also, I don't like this solution because I think it's not very sure, It's better to have a completely random code. There is a sort regular-format from malicious user point of view.
Do you know a way to generate an unique random string using, for example from an input unique key (in my case the UUIDv4)?
Otherwise, do you know some algorithm/approach to generate voucher codes?

import string
import secrets
unique_digits = string.digits
password = ''.join(secrets.choice(unique_digits) for i in range(6))
print(password)
The above code pallet generates a unique code of integers for the number of digits you want. In the above case, it will print a 6-digit unique Integer code.
If it doesn't let me know, what exactly you want.

Related

How to make one function that takes two different values ​and generates a key in Python?

For example you have two mail accounts,I want give these two e-mail accounts to a function and receive a unique key word for these two emails.But there is a problem, when I change mail locations I have to get the same result again.
def two_mails(test#gmail.com, test2#gmail.com):
#algorithm codes
return "one_key"
Another time I have to use this function and generate this key.
if two_mails(test#gmail.com, test2#gmail.com) == "one_key":
#Other codes
Even if I change email locations, I need to get the same key
if two_mails(test2#gmail.com, test#gmail.com) == "one_key":
#Other codes
They should be able to create different keys with different mails.
def two_mails(different#gmail.com, different2#gmail.com):
#algorithm codes
return "different_key"
Please write the sample code while writing your idea.
EDITED#
I need to generate a key for these two mails and use it as id in db. I need to be able to do db query with this key when I need it. two_mails(different#gmail.com, different2#gmail.com).but sometimes mails change location two_mails(different2#gmail.com, different#gmail.com). What I need is to get the same result even if the mail locations change
You can create a function which takes the email address strings and sorts them and joins them as a single string and returns the hash.
So that if the positions are interchanged it gives the same output as after the sorting operation the order of the strings are maintained all the time:
import hashlib
def getUniquekey(*, first_email, second_email):
data = "".join(sorted([first_email, second_email]))
return hashlib.md5(data.encode('utf-8')).hexdigest()
print(getUniquekey(first_email="foo#yahoo.com", second_email="bar#yahoo.com"))
print(getUniquekey(first_email="bar#yahoo.com", second_email="foo#yahoo.com"))
Output:
c8cd4cdcd95e12c043fef21c0fb07a9f
c8cd4cdcd95e12c043fef21c0fb07a9f
If you sort your 2 email adresses before creating a key by internal hashing function of the 2-uple (email1,email2), the key will be consistant.
You have restricted your function to test#gmail.com, test2#gmail.com as arguments, if what you need is to parse different arguments to your function at every call, you'll need to include parameters:
def two_mails(first_email, second_email):
#make sure to replace test#gmail.com, test2#gmail.com with first_email, second_email throughout your functio
#algorithm codes
return "one_key"
and whenever you need to call the function, you can parse whatever appropriate arguments into it

How to generate a unique auth token in python?

I am trying to write a token based auth in flask for my android app. For that I need a unique token using which I can verify the user.
Itsdangerous library provide a JSONWebSignatureSerializer function using which I can create JWT token. So my first question is, is it safe to use JWT for mobile based auth ?
Secondly, I did a little bit research on how django rest framework generates its token.
def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()
Is this token unique or just a random one? Which one should I use for a mobile based auth?
What is the based way to generate a unique token for mobile application in python ?
You can use like as mentioned the builtin uuid module. The new secrets module released in 3.6 is also capable of creating unique tokens also.
from uuid import uuid4
rand_token = uuid4()
The function below creates a unique token every time it's called. The os.urandom method returns 20 random bytes as a string and the binascii.hexlify method converts each of those 20 bytes into 2-digit hex representation of that byte. This is why the return value is twice as long.
If you want to use this approach and need tokens to be specific length, use half of the length you need as an argument to the os.urandom method.
def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()
OK, this is old, but I'm chiming in anyway. You need to decide: Do you want unique or random? Choose one.
If you want unique, use UUID. The whole purpose of UUIDs is to make sure you generate something that's unique. UUID stands for Universally Unique ID.
If you want something that's random, use os.urandom. Truly random results cannot be limited to uniqueness constraints! That'd make them not random. Indeed, it'd make them UUIDs.
Now, for your question, you're asking for an auth token. That means you're using this for security purposes. UUIDs are the wrong solution and generating a secure number is the right one. Could you have a collision when generating a random number instead of a UUID? Yes. But it's unlikely unless you've got a gazillion users. You'll want to do your math on this, but my recommendation is: Don't use UUID when you mean to use random.
Oy.
Look at the uuid() library. Docs are here:
https://docs.python.org/2/library/uuid.html
and a previous discussion of the question is here:
How to create a GUID/UUID in Python
with lots of good details.
I wrote a little helper function for generating a unique token in django models. You can call it from the save() method of your model. It generates a candidate token using a defined function, searches the existing rows in the database for that candidate token. If it finds one, it trys again, otherwise, it returns the candidate string. Note that there is a small race condition in this, but is unlikely to occur with a token function with a sufficiently large range of outputs.
def generate_unique_token(Model,
token_field="token",
token_function=lambda: uuid.uuid4().hex[:8]):
"""
Generates random tokens until a unique one is found
:param Model: a Model class that should be searched
:param token_field: a string with the name of the token field to search in the model_class
:param token_function: a callable that returns a candidate value
:return: the unique candidate token
"""
unique_token_found = False
while not unique_token_found:
token = token_function()
# This weird looking construction is a way to pass a value to a field with a dynamic name
if Model.objects.filter(**{token_field:token}).count() is 0:
unique_token_found = True
return token
Then, you can find a unique token simply by calling
token = generate_unique_token(MyModelInstance, "token_field_name")
It even supports using other methods of generating tokens. For example, if you want to use the full uuid, you can simply call it like this:
token = generate_unique_token(MyModel, "token_field_name", lambda: uuid.uuid4().hex)
A possible solution is to AES encrypt the time when the token expires + the username which makes it fairly easy to spot expired tokens and requires no extra database space for the tokens

Python random character string repeated 7/2000 records

I am using the below to generate a random set of characters and numbers:
tag = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(36)])
I thought that this was a decent method. 36 character length, with each character being one of 36 unique options. Should be a good amount of randomness, right?
Then, I was running a query off an instance with what I thought was a unique tag. Turns out, there were SEVEN (7) records with the same "random" tag. So, I opened the DB, and ran a query to see the repeatability of my tags.
Turns out that not only does mine show up 7 times, but there are a number of tags that repeatedly appear over and over again. With approximately 2000 rows, it clearly should not be happening.
Two questions:
(1) What is wrong with my approach, and why would it be repeating the same tag so often?
(2) What would be a better approach to get unique tags for each record?
Here is the code I am using to save this to the DB. While it is written in Django, clearly this is not a django related question.
class Note(models.Model):
...
def save(self, *args, **kwargs):
import random
import string
self.tag = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(36)])
super(Note, self).save(*args, **kwargs)
The problem with your approach:
true randomness/crypto is hard, you should try to use tested existing solutions instead of implementing your own.
Randomness isn't guaranteed - while 'unlikely', there's nothing preventing the same string to be generated more than once.
A better solution would be to not reinvent the wheel, and use the uuid module, a common solution to generating unique identifiers:
import uuid
tag = uuid.uuid1()
Use a cryptographically secure PRNG with random.SystemRandom(). It will use the PRNG of whatever system you are on.
tag = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for n in xrange(36))
Note that there is no need to pass this as a list comprehension to join().
There are 6236 possible combinations, a number with 65 digits, so duplicates should be extremely rare, even if you take the birthday paradox into consideration.

Algorithm for A/B testing

I need to develop an A/B testing method for my users. Basically I need to split my users into a number of groups - for example 40% and 60%.
I have around 1,000,00 users and I need to know what would be my best approach. Random numbers are not an option because the users will get different results each time. My second option is to alter my database so each user will have a predefined number (randomly generated). The negative side is that if I get 50 for example, I will always have that number unless I create a new user. I don't mind but I'm not sure that altering the database is a good idea for that purpose.
Are there any other solutions so I can avoid that?
Run a simple algorithm against the primary key. For instance, if you have an integer for user id, separate by even and odd numbers.
Use a mod function if you need more than 2 groups.
Well you are using MySQL so whether it's a good idea or not, it's hard to tell. Altering databases could be costly. Also it could affect performance in the long run if it starts getting bigger. Also you would have to modify your system to include that number in the database for every new user. You have tagged this as a python question. So here is another way of doing it without making any changes to the database. Since you are talking about users you probably have a unique identifier for all of them, let's say e-mail. Instead of email I'll be using uuid's.
import hashlib
def calculateab(email):
maxhash = 16**40
emailhash = int(hashlib.sha1(email).hexdigest(), 16)
div = (maxhash/100)-1
return int(float(emailhash/div))
#A small demo
if __name__ == '__main__':
import uuid, time, json
emails = []
verify = {}
for i in range(1000000):
emails.append(str(uuid.uuid4()))
starttime = time.time()
for i in emails:
ab = calculateab(i)
if ab not in verify:
verify[ab] = 1
else:
verify[ab] += 1
#json for your eye's pleasure
print json.dumps(verify, indent = 4)
#if you look at the numbers, you'll see that they are well distributed so
#unless you are going to do that every second for all users, it should work fine
print "total calculation time {0} seconds".format((time.time() - starttime))
Not that much to do with python, more of a math solution. You could use md5, sha1 or anything along those lines, as long as it has a fixed length and it's a hex number. The -1 on the 6-th line is optional - it sets the range from 0 to 99 instead of 1 to 100. You could also modify that to use floats which will give you a greater flexibility.
I would add an auxiliary table with just userId and A/B. You do not change existent table and it is easy to change the percentage per class if you ever need to. It is very little invasive.
Here is the JS one liner:
const AB = (str) => parseInt(sha1(str).slice(0, 1), 16) % 2 === 0 ? 'A': 'B';
and the result for 10 million random emails:
{ A: 5003530, B: 4996470 }

Best practice for two way hashing in python?

I want to allow users to validate their email address by clicking on a link. The link would look something like
http://www.example.com/verifyemail?id=some-random-string
When I am sending this email, I want to be able to easily generate this 'some-random-string' from row id of user, an integer. and when user clicks on this link, generate that integer back.
Only requirement is this 'some-random-string' should be as opaque and non-guessable to the user as possible.
Finally, this is what I settled on
def p3_encrypt_safe(plain, key):
return base64.urlsafe_b64encode(p3_encrypt(plain, key))
used the nice crypto library from http://www.nightsong.com/phr/crypto/p3.py
addition of base64 safe encoding is mine.
Use encryption, that's exactly what it's designed for. Blowfish, AES, even DES3 if you don't need particularly high security.
Alternatively, you could compute an SHA-256 or SHA-512 (or whatever) hash of the email address and store it in a database along with the email address itself. That way you can just look up the email address using the hash as a key.
Your best choice is to generate a hash (one-way function) of some of the user's data. For example, to generate a hash of user's row id, you could use something like:
>>> import hashlib
>>> hashlib.sha1('3').hexdigest()
'77de68daecd823babbb58edb1c8e14d7106e83bb'
However, basing your pseudorandom string only on a row id is not very secure, as the user could easily reverse the hash (try googling 77de68daecd823babbb58edb1c8e14d7106e83bb) of such a short string.
A simple solution here is to "salt" the hashed string, i.e. add the same secret string to every value that is hashed. For example:
>>> hashlib.sha1('3' + 'email#of.user' + 'somestringconstant').hexdigest()
'b3ca694a9987f39783a324f00cfe8279601decd3'
If you google b3ca694a9987f39783a324f00cfe8279601decd3, probably the only result will be a link to this answer :-), which is not a proof, but a good hint that this hash is quite unique.

Categories