Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disabled field is considered for validation in WTForms and Flask

I have some fields in page disabled as for example:(using jinja2 templating system)

<html>
<body>
<form action="" method=POST>
    {{ form.name(disabled=True) }}
    {{ form.title }}
    -- submit button --
</form>
</body>
</html>

Field is disabled in the form as expected.

In my views.py: On doing validate_on_submit() on form submit, it fails with validation error on 'name' field which is disabled. I was hoping that validation ignores disabled field. Is it the right behaviour? If so, can you please let know how to handle such a case?

Updated:

class TeamForm(wtf.Form):
    name = wtf.TextField("Team Name", validators=[validators.Required()])
    title = wtf.TextField("Title", validators=[validators.Required()])
like image 435
rajpy Avatar asked May 10 '13 12:05

rajpy


People also ask

Which of the following validators can be used to compare values of two form fields?

You can perform this type of form validation by using the CompareValidator control. To compare two dates, you need to set the ControlToValidate, ControlToCompare, Operator, and Type properties of the CompareValidator control.

What is Stringfield?

String field theory (SFT) is a formalism in string theory in which the dynamics of relativistic strings is reformulated in the language of quantum field theory.

What is the purpose of form Hidden_tag?

The form. hidden_tag() template argument generates a hidden field that includes a token that is used to protect the form against CSRF attacks. All you need to do to have the form protected is include this hidden field and have the SECRET_KEY variable defined in the Flask configuration.


2 Answers

This is actually an interesting problem, and the way WTForms solves it is intentionally something that requires explicitness, because it has to do with security and not allowing users to fake input.

So the intent is, that "managers" cannot edit the name, while "admins" can.

At first glance this seems obvious, just disable the field in HTML, and write your view like this:

def edit_team():
    form = TeamForm(request.POST, obj=team)
    if request.POST and form.validate():
        form.populate_obj(team) # <-- This is the dangerous part here
        return redirect('/teams')
    return render('edit_team.html')

As written, this is a major security risk, because the disabled property in HTML forms is client-side only. Anyone with an HTML inspector (ie FireBug, webkit document inspector, etc) can remove this property, or someone could simply make a request like so:

POST /edit_team/7 HTTP/1.0
Content-Type: application/x-urlencoded

team=EVILTEAMNAME&title=foo

The issue then is of course, how do we gate this properly on the server-side, corresponding to the appropriate way of doing this? The correct approach with WTForms is to not have the field in the first place. There's a few ways to do this, one is to use form composition and have e.g. ManagerTeamForm and AdminTeamForm (sometimes this is better) but other times it's easier to use del to remove specific fields.

So here's how you would write your view, and not have the validation issues:

def edit_team():
    form = TeamForm(request.POST, obj=team)
    if user.role == 'manager':
        del form.name
    if request.POST and form.validate():
        form.populate_obj(team)
        return redirect('/teams')
    return render('edit_team.html')

And a quick modification to the template:

<html>
<body>
<form action="" method=POST>
    {% if 'name' in form %}
        {{ form.name() }}
    {% else %}
        {{ team.name|e }}
    {% endif %}
    {{ form.title }}
    -- submit button --
</form>
</body>
</html>

Some pieces of reference for wtforms best-practices:

  • WTForms 'Solving Specific Problems'
  • Dangers of Using forms as a backing store (WTForms google group) Post 1 / Post 2
  • StackOverflow: WTForms 'readonly' attribute
like image 81
Crast Avatar answered Nov 05 '22 15:11

Crast


You need to make the name field optional when defining the form.

name = wtf.TextField("Team Name", validators=[validators.Optional()])

Then in your views, pass a variable called "role" and set it to either manager or admin depending on the user.

<form action="" method=POST>
{% if role == 'manager' % }
    {{ form.name(disabled=True) }}
{% else % }
    {{ form.name() }}
{{ form.title }}
-- submit button --
</form>
like image 21
codegeek Avatar answered Nov 05 '22 16:11

codegeek