emmett-framework / emmett

The web framework for inventors
BSD 3-Clause "New" or "Revised" License
1.09k stars 72 forks source link

Password fields get reprocessed on saves even without changes #432

Closed josejachuf closed 2 years ago

josejachuf commented 2 years ago

Hi @gi0baro

[emmett: 2.4.9]

Give the following model for User:

class User(AuthUser):
    tablename = 'auth_users'
    belongs_to({'institucion': 'Institucion'})

    id = Field.string(default=lambda: uuid4().hex)
    avatar = Field.upload(autodelete=True)
    cuit = Field.string(length=11)

    validation = {
            'email': {'is': 'email', 'message': 'Ingrese el Correo Electrónico'},
            'last_name': {'presence': True, 'message': 'Ingrese el Apellido'},
            'first_name': {'presence': True, 'message': 'Ingrese el Nombre'},
            'password': {'len': {'gte': 8, 'lte': 50},
                     'message': 'La contraseña debe contener entre 8 y 50 caracteres'
                     }
    }

    fields_rw = {
            'institucion': True,
            'cuit': True
            }

    rest_rw = {
        'institucion': True,
        'registration_key': True,
        'password': (False, True)
    }

And the form to create and update

@user.route("/form")
async def form():

    _id = request.query_params.id

    exclude_fields = []
    if _id:
        exclude_fields = ['password']

    form = await User.form(record_id=_id,  exclude_fields=exclude_fields)
    if form.accepted:
        if 'id' in form.params:
            _id = form.params.id

        redirect(url('.bar', params=dict(id=_id)))

    return dict(form=form)

From the form I can create a user useless, but when I edit it, the changes are not registered. It does not give error of any kind, neither in the console nor in the browser

In this way I have working on Emmett 2.3.1 (and Weppy). I can't try this in version 2.3.1 for the use of PK as UUID, but in another old project it works.

gi0baro commented 2 years ago

I need to investigate this. @josejachuf gonna update you as soon as I reproduce this.

gi0baro commented 2 years ago

@josejachuf can't reproduce this with the User module in bloggy example. I suppose this might be due to the belongs relation.

Does logging form.params and form.input_params help?

josejachuf commented 2 years ago

Hi @gi0baro

it's into if form.accepted:

form.params:

<sdict {'email': 'jjachuf@gmail.com', 'first_name': 'Jose', 'last_name': 'Jachuf', 'cuit': '123', 'institucion': 'd7cd362c70c542f0bab580be5f1f9fdc'}>

form.input_params:

<sdict {'last_name': 'Jachuf', 'institucion': 'd7cd362c70c542f0bab580be5f1f9fdc', 'email': 'jjachuf@gmail.com', 'cuit': '123', 'first_name': 'Jose', '_csrf_token': 'd2f70067-315b-41a1-a0d6-1549510e1735'}>

then update cuit is null or empty in database. This it's with postgresql

josejachuf commented 2 years ago

Any change I make is not taken at the update. However it works fine when creating new records

gi0baro commented 2 years ago

@josejachuf do you have any before or after callbacks for save or update operations? I tested this locally with the model and route you provided and it works for me.

josejachuf commented 2 years ago

Hi @gi0baro

No, I don't use any callbacks. In your test, you use PK uuid?

I did this and works fine:

     if form.accepted:
        if 'id' in form.params:
            _id = form.params.id
            row = User.get(_id)
            row.update_record(**form.params)

        redirect(url('.ficha', params=dict(id=_id)))

    return dict(form=form)
gi0baro commented 2 years ago

@josejachuf yup, tried also with uuid, works fine for me. I'm definitely missing something. Can you try adding a couple of prints like this:

if form.accepted:
    print(form.record.changes)
    print(form.record.validation_errors)
josejachuf commented 2 years ago

form.record.changes

<sdict {'cuit': ('4321', '3333'), 'updated_at': (datetime.datetime(2022, 5, 18, 12, 43, 52), DateTime(2022, 5, 18,
13, 4, 53, 413197, tzinfo=Timezone('UTC')))}>

Here's the problem: form.record.validation_errors

<sdict {'password': 'La contraseña debe contener entre 8 y 50 caracteres', 'email': 'Ingrese el Correo Electrónico'
}>

but, I do:


 exclude_fields = []
    if _id:
        exclude_fields = ['password']

And the email is included in the form

imagen

gi0baro commented 2 years ago

@josejachuf this is actually quite hard. The issue here is actually in regards of the password validation, as in 2.4 forms use the save method of models, and thus validates the entire record, not just the fields coming from the form. And this is also true for every operation made on models containing "password" fields.

I gonna need some time to understand how to solve this.

gi0baro commented 2 years ago

@josejachuf this should actually have been fixed in current master. Do you mind making a test?

josejachuf commented 2 years ago

@gi0baro , the patch works fine!

Thanks