How do run assertion against exception with unittest? - flask

I'm unittesting token creation (think PyJWT) and I need to test if
expired token raises exception:
from jwt.exceptions import ExpiredSignatureError
def test_invalid_logout_expired_token(self):
add_user('testuser', 'testemail#mail.com', 'hakunamatata')
current_app.config['TOKEN_EXPIRATION_SECONDS'] = -1
with self.client:
resp_login = self.client.post(
'/auth/login',
data=json.dumps({
'email': 'testemail#mail.com',
'password': 'hakunamatata'
}),
content_type='application/json'
)
token = json.loads(resp_login.data.decode())['auth_token']
response = self.client.get('/auth/logout', headers={'Authorization': f'Bearer {token}'})
data = json.loads(response.data.decode())
self.assertRaises(ExpiredSignatureError, data) # <-- isn't working
exception is raised correctly:
File "/usr/src/app/project/api/auth.py", line 90, in logout
resp = User.decode_auth_token(auth_token)
File "/usr/src/app/project/api/models.py", line 45, in decode_auth_token
return jwt.decode(token, current_app.config.get('SECRET_KEY'), algorithms=["HS256"])
File "/usr/local/lib/python3.8/site-packages/jwt/api_jwt.py", line 119, in decode
decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
File "/usr/local/lib/python3.8/site-packages/jwt/api_jwt.py", line 106, in decode_complete
self._validate_claims(payload, merged_options, **kwargs)
File "/usr/local/lib/python3.8/site-packages/jwt/api_jwt.py", line 142, in _validate_claims
self._validate_exp(payload, now, leeway)
File "/usr/local/lib/python3.8/site-packages/jwt/api_jwt.py", line 177, in _validate_exp
raise ExpiredSignatureError("Signature has expired")
jwt.exceptions.ExpiredSignatureError: Signature has expired
but test runner gives error:
test_invalid_logout_expired_token (test_auth.TestAuthBlueprint) ... ERROR
What is the right way to go about it ?
Update:
def test_invalid_logout_expired_token(self):
add_user('testuser', 'testemail#mail.com', 'hakunamatata')
current_app.config['TOKEN_EXPIRATION_SECONDS'] = -1
with self.client:
resp_login = self.client.post(
'/auth/login',
data=json.dumps({
'email': 'testemail#mail.com',
'password': 'hakunamatata'
}),
content_type='application/json'
)
token = json.loads(resp_login.data.decode())['auth_token']
self.assertRaises(ExpiredSignatureError, User.decode_auth_token(token))
User class:
class User:
...
#staticmethod
def decode_auth_token(token):
return jwt.decode(token, current_app.config.get('SECRET_KEY'), algorithms=["HS256"])
Replaced data with User.decode_auth_token().

Related

Django raw sql insert query in triple quotation: Django interprets null values from ajax request data as None column

I am working on a Django/React project. Using DRF, I am throwing an API route for performing SQL queries in my PostgreSQL database. But I am having issues with my current code setup.
I have setup my INSERT query in the API as raw queries (using cursor) enclosed in a triple quote multi-line string """INSERT...""", then format my values using string formatting %s. In my API, I capture each data from the request body into a variable. Everything works fine if all request data are filled. But, if it is null, Django obviously assigns None to the variable.
Now back to my sql query, Django will treat the null %s as None and as a table column instead of a correct null value, thus throwing a ProgrammingError column "none" does not exist.
Here are sample codes:
React Frontend
const [lastName, setLastName] = useState('')
const [firstName, setFirstName] = useState('')
const [middleName, setMiddleName] = useState('')
const [nameExtn, setNameExtn] = useState('')
const [sex, setSex] = useState('')
const [civilStatus, setCivilStatus] = useState('')
const [bloodType, setBloodType] = useState('')
const [height, setHeight] = useState('')
const [weight, setWeight] = useState('')
const newPersonalInfo = (token, data) => {
let endpoint = "/jobnet/api/profile/pds/basic/personal/"
let lookupOptions = {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': `Token ${token}`
},
body: JSON.stringify(data),
credentials: 'include'
}
fetch(endpoint, lookupOptions)
.then(res=>res.json())
.then(function(info){
console.log(info)
})
.catch(err=>console.log(err));
}
const handleNewPersonalInfo = () => {
newPersonalInfo(props.user.token, {
firstname: firstName,
lastname: lastName,
middlename: middleName,
extension: nameExtn,
birthdate: selectedDate,
sex: sex,
civilstatus: civilStatus,
bloodtype: bloodType,
height: height,
weight: weight,
})
}
...
return(
<Button
variant="contained"
color="primary"
onClick={handleNewPersonalInfo}
>
SAVE
</Button>
)
Django API (DRF)
class APIListCreate__PersonalInfo(generics.ListCreateAPIView):
try:
serializer_class = PDSBasicPersonalInfoSerializer
permission_classes = (jobnet_permissions.IsAuthenticated,)
authentication_classes = (knox_TokenAuthentication,)
except Exception as e:
traceback.print_exc()
def get_queryset(self):
user = self.request.user
if user and user.is_authenticated:
query = ("""
SELECT
bsinfo.firstname,
bsinfo.middlename,
bsinfo.surname,
bsinfo.birthdate,
bsinfo.sex,
bsinfo.extention,
bsinfo.civilstatus,
bsinfo.height_m,
bsinfo.weight_kg,
bsinfo.bloodtype,
bsinfo.photo_path
FROM jobnet_app.basicinfo bsinfo
WHERE
id=%s
""" % user.id)
return raw_sql_select(query, "default")
else:
return None
def get(self, request):
data = [
{
"first_name": col.firstname,
"middle_name": col.middlename,
"last_name": col.surname,
"name_extension": col.extention,
"birthdate": col.birthdate,
"sex": col.sex,
"civil_status": col.civilstatus,
"height": col.height_m,
"weight": col.weight_kg,
"blood_type": col.bloodtype,
"photo_path": col.photo_path
} for col in self.get_queryset()[1]
]
return Response(data[0])
def post(self, request):
try:
user = request.user.id
firstname = request.data.get('firstname') or ''
middlename = request.data.get('middlename') or ''
lastname = request.data.get('lastname') or ''
birthdate = request.data.get('birthdate') or ''
sex = request.data.get('sex') or ''
extension = request.data.get('extension') or ''
civilstatus = request.data.get('civilstatus') or ''
height_m = request.data.get('height') or 0
weight_kg = request.data.get('weight') or 0
bloodtype = request.data.get('bloodtype') or ''
query = ("""
START TRANSACTION;
INSERT INTO jobnet_app.basicinfo (
id,
firstname,
middlename,
surname,
birthdate,
sex,
extention,
civilstatus,
height_m,
bloodtype,
weight_kg
)
VALUES (%s,'%s','%s','%s','%s','%s','%s','%s',%s,'%s',%s);
""" % (
user,
firstname,
middlename,
lastname,
birthdate,
sex,
extension,
civilstatus,
height_m,
bloodtype,
weight_kg
)
)
unformatted_query_result = raw_sql_insert(query, "default")
if unformatted_query_result:
raw_sql_commit("default")
return Response({
"success": True,
"message": "Your basic personal information has been updated successfully."
}, status=status.HTTP_201_CREATED)
else:
raw_sql_rollback("default")
return Response({
"success": False,
"message": "There was a problem updating your personal information."
}, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
traceback.print_exc()
return Response({
"success": False,
"message":"Internal System Error: " + str(e)
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
With above setup, I get such error:
Traceback (most recent call last):
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 83, in _execute
return self.cursor.execute(sql)
psycopg2.ProgrammingError: column "none" does not exist
LINE 4: ...on','2002-03-18T06:18:45.284Z','Male','','Single',None,'B+',...
^
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:\PGADN\Projects\pgadn-v2-website\adnwebsite\reactify\utils.py", line 17, in raw_sql_insert
cn.execute(query)
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 100, in execute
return super().execute(sql, params)
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\utils.py", line 89, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 83, in _execute
return self.cursor.execute(sql)
django.db.utils.ProgrammingError: column "none" does not exist
LINE 4: ...on','2002-03-18T06:18:45.284Z','Male','','Single',None,'B+',...
I am using:
Django 2.0.6
Django Rest Framework 3.10.3
PostgreSQL 11.3 (on my development machine) and PostgreSQL 12.1 (on production server) (although the error above has not yet been replicated to the server)
Note: raw_sql_select, raw_sql_insert, raw_sql_commit, raw_sql_rollback are simply custom-made helper functions which handle the actual cursor execution in the background.
Use SQL Parameters instead of % to create your insert script.
For example-
# **WRONG**
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)" % (10, 20))
# **correct**
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)", (10, 20))
You must be using cusrsor.execute in your helper functions. Pass the values as a list as a second parameter in the cusrsor.execute as shown above.
Folow this link to read more.

Django Rest Framework Elastic Search: RequestError 400 parsing_exception

When trying to query for event documents that match this condition, I'm getting a parsing exception and I'm not sure what's causing it. This is occurring in my custom get_queryset method. In my get_query in my document view set I'm getting an error.
def get_queryset(self):
qs = super().get_queryset()
user = self.request.user
if hasattr(user, 'userprofile'):
user_universities = user.userprofile.universities.all().values_list("id")
user_universities_campus = user.userprofile.universities.all().values_list("main_campus__id")
query = query | Q('bool', must=[
Q('match', visibility_scope=Event.UNIVERSITY),
Q('bool', must=[
Q('terms', university__id=list(user_universities)),
Q('bool', should=[
Q('terms', university__main_campus__id=list(user_universities)),
Q('terms', university__main_campus__id=list(user_universities_campus))
])
])
])
qs = qs.query(query)
return qs
I'm getting this error:
if self.count == 0 and not self.allow_empty_first_page:
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\django\utils\functional.py", line 80, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\django\core\paginator.py", line 91, in count
return c()
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\elasticsearch_dsl\search.py", line 679, in count
**self._params
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\elasticsearch\client\utils.py", line 84, in _wrapped
return func(*args, params=params, **kwargs)
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\elasticsearch\client\__init__.py", line 529, in count
"POST", _make_path(index, doc_type, "_count"), params=params, body=body
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\elasticsearch\transport.py", line 358, in perform_request
timeout=timeout,
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\elasticsearch\connection\http_urllib3.py", line 261, in perform_request
self._raise_error(response.status, raw_data)
File "C:\Users\fendy\.virtualenvs\cul\lib\site-packages\elasticsearch\connection\base.py", line 182, in _raise_error
status_code, error_message, additional_info
elasticsearch.exceptions.RequestError: RequestError(400, 'parsing_exception', '[terms] unknown token [END_ARRAY] after [university.id]')
Query String printout:
Bool(should=[Bool(must=[Match(visibility_scope=2), Bool(must=[Terms(university__id=[(42809,)]), Bool(should=[Terms(university__main_campus__id=[(42809,)]), Terms(university__main_campus__id=[(None,)])])])]), Match(visibility_scope=0)])
Your query in string should be like this:
{
"bool": {
"should":[
"bool" : {
"must":[
{"match":{
"visibility_scope" : 2
},
"bool":{
"must":[
{
"terms": {
}
....
.....
.....
}
]
}
}
]
}
]
}
}
Can you update your question with a querystring? I may be able to complete it.
It is because of queryset. Please set flat=True.
user_universities = user.userprofile.universities.all().values_list("id", flat=True)

ConfigParser.NoSectionError: No section: 'options'

I tried to read a config file of odoo 8 but getting this error:
My config file looks like:
[options]
#This is the password that allows database operations:
admin_passwd = admin
db_host = False
db_port = False
db_user = kabeer
db_password = password
addons_path = /OdooSass/odoo-8.0-20151229/openerp/addons
My code looks like:
def get_odoo_config():
res = {}
if not args.get('odoo_config'):
return res
p = ConfigParser.ConfigParser()
log('Read odoo config', args.get('odoo_config'))
p.read(args.get('odoo_config'))
for (name, value) in p.items('options'):
if value == 'True' or value == 'true':
value = True
if value == 'False' or value == 'false':
value = False
res[name] = value
return res
The error is:
Traceback (most recent call last):
File "saas.py", line 118, in <module>
odoo_config = get_odoo_config()
File "saas.py", line 110, in get_odoo_config
for (name, value) in p.items('options'):
File "/usr/lib/python2.7/ConfigParser.py", line 642, in items
raise NoSectionError(section)
ConfigParser.NoSectionError: No section: 'options'
How can i fix this error?

InvalidInput error when trying to Create or Upsert a Route53 A record

When I run this boto3 to create or upsert an A record, I get the error:
File "./metakube.py", line 523, in post_create
self.route53_update_alias_record(self.fugu_editor_external_dns, fugu_elb_identifier)
File "./metakube.py", line 508, in route53_update_alias_record
'EvaluateTargetHealth': False
File "/home/pairaccount/.virtualenvs/fugui-devops/local/lib/python2.7/site-packages/botocore/client.py", line 236, in _api_call
return self._make_api_call(operation_name, kwargs)
File "/home/pairaccount/.virtualenvs/fugui-devops/local/lib/python2.7/site-packages/botocore/client.py", line 500, in _make_api_call
raise ClientError(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (InvalidInput) when calling the ChangeResourceRecordSets operation: Invalid request
Based on the boto3 documentation this looks like the correct input. We've tried a few different variations too, but we get this error when we try to Create or Upsert an A record with this method below. We have a similar method that calls change_resource_record_sets to delete an A record and it works fine.
Any ideas on what needs to be corrected?
def route53_update_alias_record(self, external_dns_name, load_balancer_identifier):
route53_client = boto3.client('route53')
hosted_zone_id = self.get_hosted_zone_id(route53_client)
response = route53_client.change_resource_record_sets(
HostedZoneId=hosted_zone_id,
ChangeBatch={
'Comment': 'upsert alias record',
'Changes': [
{
'Action': 'UPSERT',
'ResourceRecordSet': {
'Name': external_dns_name,
'Type': 'A',
'Region': 'us-east-1',
'AliasTarget': {
'DNSName': load_balancer_identifier,
'HostedZoneId': 'Z3DZXE0Q79N41H',
'EvaluateTargetHealth': False
}
}
}
]
}
)
self.logger.info("Delete route53 alias {} response: {}".format(external_dns_name, response))
you need TTL
like :
response = client.change_resource_record_sets(
HostedZoneId=hostedzoneid,
ChangeBatch={
'Comment': 'add record',
'Changes': [
{
'Action': 'UPSERT',
'ResourceRecordSet': {
'Name': name,
'Type': 'A',
'TTL': ttl,
'ResourceRecords': [
{
'Value': value
}
]
}
}
]
}
)

BackboneJS save sending GET instead of PUT

My BackboneJS models now send a GET instead of PUT when I call the model.save() method.
I have a Django back-end with django-tastypie for the REST api.
For example, I have a user model that I try to update like this:
var me = new UserModel({
id: this.user.id
});
me.fetch({
success: function (t) {
console.log(t);
t.set({
'premium': true
});
t.save({
success: function () {
alert('success')
},
error: function (m, e) {
console.log(e);
}
});
}
})
I get the following error in the console:
GET http://127.0.0.1:8000/api/v1/users/100003258103084/ 500 (INTERNAL SERVER ERROR)
in the line where I have t.save()
Is there anything I'm doing wrong here?
EDIT
Alright, the message that it shows int he line console.log(e) is as follow:
"{"error_message": "int() argument must be a string or a number, not 'dict'", "traceback": "Traceback (most recent call last):\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 192, in wrapper\n response = callback(request, *args, **kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 406, in dispatch_detail\n return self.dispatch('detail', request, **kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 427, in dispatch\n response = method(request, **kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 1131, in put_detail\n updated_bundle = self.obj_update(bundle, request=request, **self.remove_api_resource_names(kwargs))\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 1827, in obj_update\n m2m_bundle = self.hydrate_m2m(bundle)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 743, in hydrate_m2m\n bundle.data[field_name] = field_object.hydrate_m2m(bundle)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/fields.py\", line 742, in hydrate_m2m\n m2m_hydrated.append(self.build_related_resource(value, **kwargs))\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/fields.py\", line 593, in build_related_resource\n return self.resource_from_data(self.fk_resource, value, **kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/fields.py\", line 548, in resource_from_data\n return fk_resource.obj_update(fk_bundle, **data)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 1814, in obj_update\n bundle.obj = self.obj_get(request, **lookup_kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/tastypie/resources.py\", line 1752, in obj_get\n base_object_list = self.get_object_list(request).filter(**kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/query.py\", line 621, in filter\n return self._filter_or_exclude(False, *args, **kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/query.py\", line 639, in _filter_or_exclude\n clone.query.add_q(Q(*args, **kwargs))\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/sql/query.py\", line 1250, in add_q\n can_reuse=used_aliases, force_having=force_having)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/sql/query.py\", line 1185, in add_filter\n connector)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/sql/where.py\", line 69, in add\n value = obj.prepare(lookup_type, value)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/sql/where.py\", line 320, in prepare\n return self.field.get_prep_lookup(lookup_type, value)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/fields/related.py\", line 137, in get_prep_lookup\n return self._pk_trace(value, 'get_prep_lookup', lookup_type)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/fields/related.py\", line 210, in _pk_trace\n v = getattr(field, prep_func)(lookup_type, v, **kwargs)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py\", line 310, in get_prep_lookup\n return self.get_prep_value(value)\n\n File \"/home/mohamed/code/skempi/venv/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py\", line 537, in get_prep_value\n return int(value)\n\nTypeError: int() argument must be a string or a number, not 'dict'\n"}"
Does this mean that the problem is in Django-Tastypie and not with BackbonejS? Still this GET is the issue i guess!
The save method takes 2 arguments: model.save([attributes], [options]) - both optional.
Right now you are passing the options hash instead of the attributes. Try:
t.save(t.attributes, {
success: function () {
alert('success')
},
error: function (m, e) {
console.log(e);
}
});
or
t.save({'premium':true}, {
success: function () {
alert('success')
},
error: function (m, e) {
console.log(e);
}
});
It seems that me.fetch sends GET request. There is nothing in Backbone that would issue GET request on save unless you have overridden the default sync method.
Save does only POST or PUT requests (POST if model doesn't have id and PUT otherwise).