Cleaner way to get user name in pylons with repoze.name - python

in mako template, i use this
${request.environ['repoze.who.identity']['user']}
and the render in controller:
render('file.html')
can i write this better without passing in parameter everytime?

Well, you can auto add the varible in the base controller in /lib/base.py. This will add it to every controller in your pylons application automatically. I'm using repoze.what and what I do is in base.py I put:
# if there's no user set, just setup a blank instance
c.current_user = auth.get_user(User())
And that's just a convienence function I wrote into an auth lib. User() is a blank instance of the User model so that the template has something and won't throw a invalid key error.
def get_user(default):
"""Return the user object from the `repoze.who` Metadata Plugin
:param default: default item to send back if user not logged in
Since we might not be logged in and template choke on trying to output
None/empty data we can pass in a blank User object to get back as a default
and the templates should work ok with default empty values on that
"""
if 'repoze.who.identity' in request.environ:
return request.environ['repoze.who.identity']['user']
else:
return default

Related

How should I write view unittest in Django?

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.

next_is_valid() doesn't exist in flask-login?

Flask-login doc says we should validate next using next_is_valid(), but I can't find any such method:
Warning: You MUST validate the value of the next parameter. If you do not, your application will be vulnerable to open redirects.
#app.route('/login', methods=['GET', 'POST'])
def login():
# Here we use a class of some kind to represent and validate our
# client-side form data. For example, WTForms is a library that will
# handle this for us.
form = LoginForm()
if form.validate_on_submit():
# Login and validate the user.
login_user(user)
flask.flash('Logged in successfully.')
next = flask.request.args.get('next')
if not next_is_valid(next):
return flask.abort(400)
return flask.redirect(next or flask.url_for('index'))
return flask.render_template('login.html', form=form)
Running this I get the error:
NameError: global name 'next_is_valid' is not defined
And if I do:
from flask.ext.login import next_is_valid
>> ImportError: cannot import name next_is_valid
Where is the next_is_valid() function, and if it doesn't exist, how do I validate the next parameter?
It doesn't say you must validate next with next_is_valid, only that
You MUST validate the value of the next parameter.
next_is_valid is just an example function.
You must determine if next is valid based on your own criteria. next is the url to redirect to on a successful login. If you have any permissions to apply or restrictions on your site this is where you would want to make sure they are enforced.
For example, a user could attempt to login with the request url http://example.com/login?next=admin/delete/all/users. If the login attempt was successful and the admin permission was not checked within your login function or on the endpoint itself, well, bad things could happen. It all depends on how you structure your application and control access to individual endpoints.
In addition to the examples mentioned by other answers, next could also be next=malicioussite.url. If a malicious actor sent someone to your login link with next set to their malicious site, then after logging in the user could think they are still on your site, but instead have been directed somewhere dangerous. Make sure to validate next against redirecting to other websites.

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.

How do I pass template values when doing a redirect?

I use the blobstoreuploadhandler and hence must return a self.redirect but I need to pass values to my template. How can I do it? If I can't use template values then I suppose I can use session variables and I've included the beaker session library but I can't understand how to access the session variables in django template. Any idea how I should do it?
I use default builtin django with google app engine and I can access session variables with a request handler but I don't understand how to do it in templates:
class Sessiontest(webapp.RequestHandler):
def get(self):
# Get the session object from the environ
self.session = self.request.environ['beaker.session']
# Check to see if a value is in the session
if 'counter' in self.session:
counter = self.session['counter'] + 1
self.session['counter'] = counter
else:
self.session['counter'] = 1
counter = 1
self.session.save()
self.response.out.write('counter: %d' % counter)
Thanks
Update/edit: My problem is almost exactly like this Accessing session variable in Django template with Google App Engine (Webapp) - Python but with the library beaker instead of gaeutilities
Update: Here's some of the code. we see that using HTTP GET to pass the values won't be very good since there's an anti-spam test that should hide the values:
def post(self, view):
message = ''
challenge = self.request.get('recaptcha_challenge_field').encode('utf-8')
response = self.request.get('recaptcha_response_field').encode('utf-8')
remoteip = os.environ['REMOTE_ADDR']
cResponse = captcha.submit(
challenge,
response,
CAPTCHA_PRV_KEY,
remoteip)
if cResponse.is_valid:
isHuman=True
else:#failed anti-spam test and can try again
isHuman=False
#Reprint the form
import util
template_values = {'isHuman':isHuman,'user' : users.get_current_user(),}
template_values.update(dict(current_user=self.current_user, facebook_app_id=FACEBOOK_APP_ID))
template_values.update(dict(capture=captcha.displayhtml(public_key = CAPTCHA_PUB_KEY, use_ssl = False, error = None)))
path = os.path.join(os.path.dirname(__file__), 'market', 'market_insert.html')
self.redirect("/ai") # Here the values aren't passed on and I must make a redirect
If you are doing a redirect you might have to redirect with the variables that you wish to keep in the GET string. So you redirect from
/myview/
to
/myview2/?variable1=value
However, I think you should really look to see why you are doing redirects. I tend to do them after a POST to a form, and if the user needs to be logged on, I redirect to a login screen with
/authentication/login/?next=/view/they/wanted/to/see
Otherwise you could keep things in cookies but its not the best way to proceed.
How about letting your class inherit from multiple classes, both requesthandler class and blobstoreuploadhandler, in that way you can both render your template with values with the functions in the requesthandler, and use the functions in blobstoreuploadhandler?
A class definition with multiple base classes looks as follows:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
...
<statement-N>

files getting wrongly cached by the web server while using standard python file operations (Django)

I have a Django app, which populates content from a text file, and populates them using the initial option in a standard form. The file gets updated on the server, but when the form gets refreshed, it picks up content from the a previously saved version, or the version before the Apache WebServer was reloaded.
This means that the file is getting cached, and the content is picked up from a wrong cache and not the new file.
Here is my code. How do I ensure that everytime, spamsource function picks up the content from the most recently saved file, instead of from a cache.
def spamsource():
try:
f= open('center_access', 'r')
read=f.read()
# some manipulation on read
f.close()
return read
except IOError:
return "prono.nr"
class SpamForm(forms.Form):
domains =forms.CharField(widget=forms.Textarea(attrs=attrs_dict),
label=_(u'Domains to be Banned'), initial= spamsource())
def function(request):
# It writes the file center_access based on the changes in the textbox domains
The issue is simply that all the parameters to fields are evaluated at form definition time. So, the initial value for domains is set to whatever the return value is from spamsource() at the time the form is defined, ie usually when the server is started.
One way of fixing this would be to override the form's __init__ method and set the initial value for domains there:
class SpamForm(forms.Form):
domains = ...
def __init__(self, *args, **kwargs):
super(SpamForm, self).__init__(*args, **kwargs)
self.fields['domains'].initial = spamsource()
Alternatively, you could set the initial value when you instantiate the form in your view:
form = SpamForm(initial={'domains': spamsource()})
I fixed this. The problem was the presence of all the caching middleware in settings.py, which were being used to speed up another side of the web app.

Categories

Resources