How should I write view unittest in Django? - python

I want to write a unittest for this method in Django.
def activate(request):
id = int(request.GET.get('id'))
user = User.objects.get(id=id)
user.is_active = True
user.save()
return render(request, 'user_authentication/activation.html')
I wrote sth like this:
def test_activate_view(self):
response = self.client.get('/activation', follow=True)
self.assertTemplateUsed(response, 'user_authentication/activation.html')
It doesn't work because I get an error:
id = int(request.GET.get('id'))
TypeError: int() argument must be a string or a number, not 'NoneType':
What should I change in my test ?

Your view reads data from request.GET - you need to pass this data:
response = self.client.get('/activation?id=1', follow=True)
You also fetch this user afterwards from your database. Therefor you need to load some fixture data. Create a fixture using manage.py dumpdata and load it in your unit test like that:
class UserTestCase(TestCase):
fixtures = ['fixture.json', 'users']
Read the docs about loading fixtures for detailed explanations.
Note regarding your approach
You should not use the users id for this use case. One can easily guess this id and activate accounts.
Someone might register once with a valid email address, receive your link with the id inside and can afterwards create a bunch of accounts without providing a valid email address.
Instead you might generate a unique and random secret (aka token) and associate this token with the user. Your view should accept those tokens and resolve the user based on it. This way one can no longer easily activate.

Related

Flask back-end returning invalid data

I'm building a Flask backend. I have this route that should return the ID that matches the email
#app.route('/login', methods=['POST'])
def login():
email = request.json['data']
id = session.query(Users).filter_by(email=Users.estudent_email)
result = users_schema.dump(id)
return jsonify(result)
I'm currently sending it this data
{
"data": "name.lastname#email.com"
}
But it returns a whole bunch of data which is incorrect. It should return a single ID from the database that is assigned to the user that holds the email. What might be the cause? I'm using Marshmallow and SQLAlchemy ORM.
The problem is probably in this line:
id = session.query(Users).filter_by(email=Users.estudent_email)
You seem to have the equality check the wrong way around. Users.estudent_email references the entire field of the database, and email is a named argument in filter_by, not email that you defined in the previous line.
I can't test but I'm thinking you want:
id = session.query(Users).filter(Users.estudent_email==email)
or
id = session.query(Users).filter_by(email=email)
As for the serialization, and result = users_schema.dump(id), I have no idea because there is not enough context given to be sure how to make that work.

How do you validate application logic using Pyramid's Colander?

So far I am using colander to validate the data in my aiohttp application.
The problem I face is that I don't know how to do "deep" validation.
Given the following schema:
import colander
class User(colander.MappingSchema):
username = colander.SchemaNode(colander.String())
password = colander.SchemaNode(colander.String())
confirmation = colander.SchemaNode(colander.String())
I do both validate that the input datastructure has all the required fields are there (constraints are minimal for the sake of clarity) but I also need
to check that:
username is not already taken by another user
password and confirmation are the same
So in my controllers, the code looks like the following pseudo code:
def create_user(request):
user = await request.json()
schema = User()
# do schema validation
try:
user = schema.deserialize(user)
except colander.Invalid, exc:
response = dict(
status='error',
errors=errors.asdict()
)
return json_response(response)
else:
# check password and confirmation are the same
if user['password'] != user['confirmation']:
response = dict(
status='error'
errors=dict(confirmation="doesn't match password")
)
return json_response(response)
# check the user is not already used by another user
# we want usernames to be unique
if user_exists(user['user']):
response = dict(
status='error',
errors=dict(username='Choose another username')
)
return json_response(response)
return json_response(dict(status='ok'))
Basically there is two kinds of validation. Is it possible to have both logic in single colander schema? Is it a good pattern?
Obviously it's a matter of taste but IMHO it's better to keep data validation separate from application logic.
You'll also run into a few problems trying to confirm that the username is unique:
Colander would need to have knowledge about your application eg. get access to the database connection to check with the database that that username doesn't exist.
Colander (AFAIK) isn't setup for asyncio programming so it'll have problems coping with an async method which checks the user exists.
You really want to user creation to be ACID, so simultaneous calls to create_user with the same username cannot possibly create two users with the same username.
Checking passwords match is another story, that doesn't require any knowledge about the rest of the world and should fairly trivial with colander. I'm not expert on colander, but it looks like you can use a deferred validator to check the two passwords match.
A few other notes on your code:
create_user should be an async method
I don't know anything about your db, but to get any advantage from async programming user_exists should be async too
The user existence check should be wrapped into the ACID user creation. Eg. you should use postgres's on conflict or equivalent to catch a duplicate user as you create them rather than checking they exist first
To be properly restful and make testing easier your view should return the correct http response code on an error (currently you return 200 for all states). You should use 201 for created, 400 for invalid date and 409 or a username conflict.

Querying information for HTTPBasicAuth

For my site for auth I'm using https://flask-httpauth.readthedocs.io/en/latest/ . Now I'm trying to make it that it's using data from database. To do that i created database named Users and created columns named username and password.
To get data from this table after defining its class as model I've made get_user functions which looks like it:
#staticmethod
def get_user():
query = (Users
.select())
user = []
for s in query:
user.append(s)
return user
(I'm not sure if it's correct)
Next I had to modify get_pw function but I also wasn't sure how to modify it so I made it look like it:
#auth.get_password
def get_pw(username):
if username in Users.get_user():
return users.get(Users.get_user())
return None
Now after running the site I get prompt to give login and password but those that I set up in my database doesn't seem to work so there must be a problem with get_pw function. Also I'm using peewee SQL to manage database : http://docs.peewee-orm.com/en/latest/peewee/querying.html
You can get rid of your get_user method since you are issuing a very large select query that fetches all records from user table. The get_pw can be redefined as:
def get_pw(username):
user = Users.get(Users.name == username) #assuming you have a username field in model
return user.password #assuming a password field
Also, its a good practice to define your model class as a singular noun rather than plural. So, its better to call it User rather than Users.
This'll help you get started in no time: http://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstart

Flask crashes with ValueError after user successfully logs in using Github-Flask

I'm trying to modify this existing app (miguelgrinberg/microblog) to utilize Github-flask login (cenkalti/github-flask). It uses Flask-Login to manage the user sessions (maxcountryman/flask-login).
TraceBack: https://gist.github.com/CoinGame/4a6b14d0213850b29979
Views.py: https://gist.github.com/CoinGame/22d8098ccabc255bd5cf
The login process seems to work. I'm getting the github token which lets me make API calls for the user object. Though when I try to put all the pieces together I get this flask breaking error.
I'll admit I'm somewhat new to python and totally trying to work my way from scratch through flask and its extensions. Working on an existing project has helped me understand the framework, but I feel it may also be causing me to overlook things.
Any idea what I'm missing here to get this error after the user logs in?
the id you are passing to this function
def load_user(id):
return User.query.get(int(id))
is causing the issue. It looks like it doesnt exist or it may contain chars that arent numeric. Somewhere along the line the user_id seems to be getting lost/changed and throwing the error.
File "/root/votr-blog/app/views.py", line 21, in load_user
return User.query.get(int(id))
ValueError: invalid literal for int() with base 10: 'None'
If you can traceback where the id gets lost it might help.
You could also wrap the call to int() using try/catch
try:
int_val = int(id)
except ValueError:
print("Failure w/ value " + id)
Looking into flask-github and this code
#app.route('/github-callback')
#github.authorized_handler
def authorized(oauth_token):
next_url = request.args.get('next') or url_for('index')
if oauth_token is None:
flash("Authorization failed.")
return redirect(next_url)
#this code below is getting the user by their github access token
user = User.query.filter_by(github_access_token=oauth_token).first()
if user is None:
user = User(oauth_token)
db_session.add(user)
user.github_access_token = oauth_token
db_session.commit()
return redirect(next_url)
authorises and returns users in relation to their github access token rather than a user_id, this seems to be in conflict with login_manager with returns users by id. Your issue seems to be that the two methods are trying to do the same this.
Flask-login uses user_ids to manage sessions and users within the session. A user logs in and if authenticated that users id is saved to the session. This is used to reload the user object from the user ID stored in the session.
Github-flask uses tokens to manage users. It saves the token in the session by the looks of things. The two are conflicting.
You could do a couple of things:
1) Choose one or the other to manage users.
2) Once a user has been authenticated with github-flask, get that users id and save that to the session, so that the load_user() function has something to work with. This seems contradictory though, getting the user with github-flask just so as to get the users id so you can get the user again with flask-login.

Include authenticated user in dictionary for all views

I am working through the Pyramid authorization tutorial and I have noticed the pattern where
logged_in = request.authenticated_userid
is added to each view dictionary. Can it be avoided? I.e. is there a configuration which automatically ads user id to each view. Or is there a way to create a base, abstract view with the user id and inherit from it?
Part of the code from the tutorial:
#view_config(context='.models.Page', renderer='templates/view.pt', permission='view')
def view_page(context, request):
# not relevant code
return dict(page = context, content = content, edit_url = edit_url,
logged_in = request.authenticated_userid)
#view_config(name='add_page', context='.models.Wiki', renderer='templates/edit.pt',
permission='edit')
def add_page(context, request):
# not relevant code
return dict(page=page, save_url=save_url,
logged_in=request.authenticated_userid)
It's been awhile since I last looked, but I think logged_in in the samples is just an example to use to conditionally check if there is a logged on user or not. You could probably just as easily refer to request.authenticated_userid within any of your views or templates, too, and get the same behavior and not have to explicitly add a status to the response dict. The request object should be available to be referenced in your view templates, too.
Alternatively, I've used their cookbook to add a user object to the request to make a friendly request.user object that I can use to both check for logged in status where needed, plus get at my other user object details if I need to as well.

Categories

Resources