unable to write test for django model containing custom model field - django

I have a model for users where in the field for password is a custom field. This model works fine but i'm not able to run tests for the model.
my model
from core_engine.CustomModelFields import *
class Users(models.Model):
username = models.EmailField(unique=True)
name = models.CharField(max_length=100)
password = EncryptedField(max_length=500)
in my core_engine.CustomModelFields.py file
from account_engine.crypto import *
class EncryptedField(CharField):
def from_db_value(self, value, expression, connection, context):
value = Password.decrypt(str(value))
return value.decode("utf-8")
def to_python(self, value):
if not value:
return None
return value
#Only need to decrypt if password already encrypted.
try:
if Password.encrypt(Password.decrypt(value)) == value:
value = Password.decrypt(str(value))
return value.decode("utf-8")
except:
return value
def get_db_prep_save(self, value, connection):
value = Password.encrypt(str(value))
return value
and finally in accounts_engine.crypto.py file i have
import base64, hashlib
from django.db import models
from Crypto import Random
from Crypto.Cipher import AES
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-s[-1]]
class Password(models.Model):
def encrypt( raw ):
mysecretpassword = 'somepassword'
KEY = hashlib.sha256(mysecretpassword.encode('utf-8')).digest()
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( KEY, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
def decrypt( enc ):
mysecretpassword = 'somepassword'
KEY = hashlib.sha256(mysecretpassword.encode('utf-8')).digest()
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(KEY, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[16:] ))
All i want to do is just test my Users model and see that i'm able to create a user during test, which will be required for other tests
so in my test.py file i have
class UsersTestCase(TestCase):
#classmethod
def setUp(self):
print(dt.datetime.now())
self.user= Users.objects.create(
username = 'test#test.com',
date_first_registered = dt.datetime.now(),
password = Password.encrypt('abc')
)
def test_get_user(self):
first_customer = Users.objects.first()
self.assertEqual(first_customer.username, 'test#test.com')
On running the above test, i get an error stating :
TypeError: Object type <class 'str'> cannot be passed to C code
Edit : i understand that the error is due to me passing the password as Password.encrypt('abc').
what changes should i make to my test function in order to create to create a new user
TRACEBACK
Traceback (most recent call last):
File "D:\project_path\account_engine\tests\tests_models.py", line 15, in setUp
password = Password.encrypt('abc')
File "D:\project_path\account_engine\crypto.py", line 21, in encrypt
return base64.b64encode( iv + cipher.encrypt( raw ) )
File "d:\project_path\venv\lib\site-packages\Crypto\Cipher\_mode_cbc.py", line 178, in encrypt
c_uint8_ptr(plaintext),
File "d:\project_path\venv\lib\site-packages\Crypto\Util\_raw_api.py", line 144, in c_uint8_ptr
raise TypeError("Object type %s cannot be passed to C code" % type(data))
TypeError: Object type <class 'str'> cannot be passed to C code

Encrypt method accepts bytes string type and not str in Python 3:
Plaintexts and ciphertexts (input/output) can only be bytes, bytearray or memoryview. In Python 3, you cannot pass strings. In Python 2, you cannot pass Unicode strings.
You need to encode raw first:
def encrypt( raw ):
mysecretpassword = 'somepassword'
KEY = hashlib.sha256(mysecretpassword.encode('utf-8')).digest()
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( KEY, AES.MODE_CBC, iv )
return base64.b64encode(iv + cipher.encrypt(raw.encode('utf-8')))

Related

Django: Reimplementing Argon2PasswordHasher with key

In my Django project, I need to modify Argon2PasswordHasher to be able to encrypt with the key (SECRET_KEY) imported from settings.py.
I learned that key should be an optional parameter in Argon2, so I have to introduce it in this algorithm.
I tried to modify it including a key param in the params method but it didn't work.
How can I do?
class Argon2PasswordHasher(BasePasswordHasher):
Secure password hashing using the argon2 algorithm.
This is the winner of the Password Hashing Competition 2013-2015
(https://password-hashing.net). It requires the argon2-cffi library which
depends on native C code and might cause portability issues.
"""
algorithm = 'argon2'
library = 'argon2'
time_cost = 2
memory_cost = 102400
parallelism = 8
def encode(self, password, salt):
argon2 = self._load_library()
params = self.params()
data = argon2.low_level.hash_secret(
password.encode(),
salt.encode(),
time_cost=params.time_cost,
memory_cost=params.memory_cost,
parallelism=params.parallelism,
hash_len=params.hash_len,
type=params.type,
)
return self.algorithm + data.decode('ascii')
def decode(self, encoded):
argon2 = self._load_library()
algorithm, rest = encoded.split('$', 1)
assert algorithm == self.algorithm
params = argon2.extract_parameters('$' + rest)
variety, *_, b64salt, hash = rest.split('$')
# Add padding.
b64salt += '=' * (-len(b64salt) % 4)
salt = base64.b64decode(b64salt).decode('latin1')
return {
'algorithm': algorithm,
'hash': hash,
'memory_cost': params.memory_cost,
'parallelism': params.parallelism,
'salt': salt,
'time_cost': params.time_cost,
'variety': variety,
'version': params.version,
'params': params,
}
def verify(self, password, encoded):
argon2 = self._load_library()
algorithm, rest = encoded.split('$', 1)
assert algorithm == self.algorithm
try:
return argon2.PasswordHasher().verify('$' + rest, password)
except argon2.exceptions.VerificationError:
return False
def safe_summary(self, encoded):
decoded = self.decode(encoded)
return {
_('algorithm'): decoded['algorithm'],
_('variety'): decoded['variety'],
_('version'): decoded['version'],
_('memory cost'): decoded['memory_cost'],
_('time cost'): decoded['time_cost'],
_('parallelism'): decoded['parallelism'],
_('salt'): mask_hash(decoded['salt']),
_('hash'): mask_hash(decoded['hash']),
}
def must_update(self, encoded):
decoded = self.decode(encoded)
current_params = decoded['params']
new_params = self.params()
# Set salt_len to the salt_len of the current parameters because salt
# is explicitly passed to argon2.
new_params.salt_len = current_params.salt_len
update_salt = must_update_salt(decoded['salt'], self.salt_entropy)
return (current_params != new_params) or update_salt
def harden_runtime(self, password, encoded):
# The runtime for Argon2 is too complicated to implement a sensible
# hardening algorithm.
pass
def params(self):
argon2 = self._load_library()
# salt_len is a noop, because we provide our own salt.
return argon2.Parameters(
type=argon2.low_level.Type.ID,
version=argon2.low_level.ARGON2_VERSION,
salt_len=argon2.DEFAULT_RANDOM_SALT_LENGTH,
hash_len=argon2.DEFAULT_HASH_LENGTH,
time_cost=self.time_cost,
memory_cost=self.memory_cost,
parallelism=self.parallelism,
)

How to save result of a function into a string so it can be sent via email (using smtplib and MIME)?

I am new to Python and very much a rookie. I am trying to write a program that uses the requests module to make a request to the Dark Sky API for a weather forecast, and then uses smtplib to send that forecast in an email to myself. I have truncated my code to only show the relevant parts. I have been unable to find any answers so far so I apologise if this is a duplicate. The below code will print the function to the console without any issues, but when I try to assign it to the "body" variable and email it, the email is blank. Or if I use str(ds.current)) the email just has "none" as the body text.
How can I make it work so that the body text of the email contains the forecast that has been requested from the API? Many thanks in advance, and sorry for any rookie errors.
import requests
import json
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
class Darksky():
r = requests.get('https://api.darksky.net/forecast/e01635ccacb5548e3d1fa40403bbb3a5/-45.0312,168.6626?units=ca')
wx_json = r.json()
def __init__(self, source):
self.source = source
print "\n", "-" * 20, source, "-" * 20, "\n"
def current(self):
def summary():
return "CURRENT WEATHER:"
x = self.wx_json['currently']['summary']
return x
# I have tried using print instead of return but that did not work either.
def temp():
return "TEMPERATURE:"
y = self.wx_json['currently']['temperature']
return y
summary()
temp()
ds = Darksky("DARKSKY WX")
fromaddr = "watsonthevirtualbutler#gmail.com"
toaddr = "matt#peakpixel.nz"
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "YOUR DAILY WEATHER, SIR."
body = ds.current()
# This is where I am trying to save the function result as a string that can be emailed.
# I have tried using "str(ds.current())" but that just returns "none".
print body
msg.attach(MIMEText(body, 'plain'))
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(fromaddr, "virtualbutler")
text = msg.as_string()
msg = "CAN YOU HEAR ME, SIR?"
server.sendmail(fromaddr, toaddr, text)
server.quit()
Your summary() and temp() has two return operators, while only one return is acceptable. If you want your_function() to return few values, you can do something like: return {"first_parameter": "first_value", "second_parameter": "second_value"} and then call each value as your_function()["first_parameter"] or your_function()["second_parameter"]
Try following code and let me know the result:
class Darksky():
r = requests.get('https://api.darksky.net/forecast/e01635ccacb5548e3d1fa40403bbb3a5/-45.0312,168.6626?units=ca')
wx_json = r.json()
def __init__(self, source):
self.source = source
print "\n", "-" * 20, source, "-" * 20, "\n"
def current(self):
return "CURRENT WEATHER: {0}. TEMPERATURE: {1}".format(self.wx_json['currently']['summary'], self.wx_json['currently']['temperature'])
ds = Darksky("DARKSKY WX")
body = ds.current()

Using Python Tkinter .config() method

I am trying to use the Python Tkinter .config() method to update some message text. I can't get it to work. What might I be doing wrong (see the update_message method):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
import threading
import Queue
# styles
BACKROUND_COLOR = '#000000'
TYPEFACE = 'Unit-Bold'
FONT_SIZE = 50
TEXT_COLOR = '#777777'
TEXTBOX_WIDTH = 400
# text
TITLE = 'listen closely'
SCORE_MESSAGE = 'your score:\n '
END_MESSAGE = 'too loud!\ntry again'
# configuration
DEVICE = 'hw:1' # hardware sound card index
CHANNELS = 1
SAMPLE_RATE = 8000 # Hz // 44100
PERIOD = 256 # Frames // 256
FORMAT = aa.PCM_FORMAT_S8 # Sound format
NOISE_THRESHOLD = 3
class Display(object):
def __init__(self, parent, queue):
self.parent = parent
self.queue = queue
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(
parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title(TITLE)
parent.configure(background=BACKROUND_COLOR)
parent.displayFont = tkFont.Font(family=TYPEFACE, size=FONT_SIZE)
self.process_queue()
def process_queue(self):
try:
score = self.queue.get(0)
self.print_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def update_queue(self):
try:
score = self.queue.get(0)
self.update_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def print_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message = tk.Message(
self.parent, text=displayString, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR, width=TEXTBOX_WIDTH, justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def update_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message.config(text=displayString)
def setup_audio(queue, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol = audioop.rms(data, 2)
scaled_vol = max_vol // 4680
print scaled_vol
if scaled_vol <= 3:
# Too quiet, ignore
continue
queue.put(scaled_vol)
except audioop.error, e:
if e.message != "not a whole number of frames":
raise e
def main():
root = tk.Tk()
queue = Queue.Queue()
window = Display(root, queue)
stop_event = threading.Event()
audio_thread = threading.Thread(target=setup_audio,
args=[queue, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
pass
if __name__ == '__main__':
main()
I don't want to be laying down a new message every time I update. If the .config() doesn't work, is there another method to update the text configuration of the message?
I would use string variables, first create your string variable then set it to want you want it to display at the start next make your object and in text put the sting variable then when you want to change the text in the object change the string variable.
self.messaget = StringVar()
self.messaget.set("")
self.message = tk.Message(
self.parent, textvariable=self.messaget, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR,
width=TEXTBOX_WIDTH, justify="c").grid()
#note renember to palce the object after you have created it either using
#.grid(row = , column =) or .pack()
#note that it is textvariable instead of text if you put text instead it will run but
#but will show PY_Var instead of the value of the variable
edit
to change the text without recreating the object you do the name of the string variable you have used and .set
self.messaget.set("hi")

flask + wtforms nameerror

flask + wtforms
Hello, I have some problems with the transfer of data into a form
def edit_comment(n):
idlist = str(n)
if (r.exists('entries:%s' %idlist) != True):
return abort(404)
if 'user_id' not in session:
return abort(401)
if (g.user['group_access'] == '1'):
return abort(403)
form = EditForm(idlist)
return render_template('edit_comment.html',idlist = idlist, r = r, form = form)
...
class EditForm(Form):
edit_title = TextField("Title",validators = [Required()] ,default =r.hget('entries:%s' %idlist, 'title'))
edit_text = TextAreaField("Text",validators = [Required()],default =r.hget('entries:%s' %idlist, 'text'))
...
Traceback (most recent call last):
File "run.py", line 129, in <module>
class EditForm(Form):
File "run.py", line 130, in EditForm
edit_title = TextField("Title",validators = [Required()] ,default =r.hget('entries:%s' %idlist, 'title'))
NameError: name 'idlist' is not defined
here there are clear problems with data transmission. tried to pass through the constructor, but so far No results
You need to set the default value on the EditForm instance. Right now it' set at import time - clearly not what you want, even if the variable was defined. Actually, you don't even need the default field for it - just set it directly:
form = EditForm()
form.edit_title.data = r.hget('entries:%s' % idlist, 'title')
form.edit_text.data = r.hget('entries:%s' % idlist, 'text')
return render_template('edit_comment.html', idlist=idlist, r=r, form=form)
Note: Usually it's a good idea to have your view function to have a structure similar to this:
form = EditForm()
if form.validate_on_submit():
# do whatever should be done on submit, then redirect somewhere
return redirect(...)
elif request.method == 'GET':
# Populate the form with initial values
form.edit_title.data = ...
form.edit_text.data = ...
return render_template(..., form=form)
That way whatever the user entered is preserved in case the validation fails, but if he opens the form for the first time it's populated with whatever default data (e.g. the current values from your db) you want.

coercing to Unicode: need string or buffer, NoneType found

I am making an Ajax request into views as follows:
def all_json_models(request):
data = {}
try:
isp = request.GET['status']
present_isp = Priority.objects.filter(ispname = isp)
isp_count = MultiWAN.objects.all()
# data['latest_no_rules'] = latest_no_rules
#data['present_isp'] = present_isp
data['isp_count'] = isp_count
return HttpResponse(simplejson.dumps(data))
my models.py is like
class MultiWAN(models.Model):
isp_name = models.CharField(max_length=10)
description = models.TextField(null=True)
ip_address = models.IPAddressField(null=True)
subnet = models.IPAddressField(null=True)
gateway = models.IPAddressField(null=True)
nameserver = models.ForeignKey('NameServer')
weight = models.IntegerField(null=False)
interface = models.CharField(max_length=5)
def __unicode__(self):
"""
This function is to return the values we required.
Arguments:
- `self`:
"""
# return u'%s ' % (self.isp_name)
class NameServer(models.Model):
""" A Isp can have more than one nameserver so far we are declearing a seperate table
"""
name = models.IPAddressField(null=False)
class Priority(models.Model):
priority = models.IntegerField(null = True)
ispname = models.ForeignKey('MultiWAN')
rule = models.CharField(max_length=5,null=False)
From = models.IPAddressField(null=True)
To = models.IPAddressField(null=True)
def __unicode__(self):
return u'%s ' % (self.priority)
while making request i am getting the error:
"coercing to Unicode: need string or buffer, NoneType found"
What i am doing wrong here?
It's hard to tell without the full traceback (because it gives information about where in you code the exception is thrown).
The error message "coercing to Unicode: need string or buffer, NoneType found" means that, at some point, django tried to convert something to unicode and expected a string, but received None. This means that either you call a function passing None instead of a string, or one of you methods returns None instead of a string.
In the code you showed us, MultiWAN.__unicode__ seems ill-defined. Maybe the error stems from this ?