1
0
Fork 0

feat: expose User password for editing in master views

This commit is contained in:
Lance Edgar 2024-08-14 15:55:10 -05:00
parent 230e2fd1ab
commit 330ee324ba
6 changed files with 56 additions and 7 deletions

View file

@ -674,6 +674,13 @@ class Form:
schema = SQLAlchemySchemaNode(self.model_class,
includes=includes)
# fill in the blanks if anything got missed
for key in fields:
if key not in schema:
log.warning("key '%s' not in schema", key)
node = colander.SchemaNode(colander.String(), name=key)
schema.add(node)
else:
# make basic schema

View file

@ -31,12 +31,15 @@ in the namespace:
* :class:`deform:deform.widget.Widget` (base class)
* :class:`deform:deform.widget.TextInputWidget`
* :class:`deform:deform.widget.TextAreaWidget`
* :class:`deform:deform.widget.PasswordWidget`
* :class:`deform:deform.widget.CheckedPasswordWidget`
* :class:`deform:deform.widget.SelectWidget`
* :class:`deform:deform.widget.CheckboxChoiceWidget`
"""
import colander
from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
PasswordWidget, CheckedPasswordWidget,
SelectWidget, CheckboxChoiceWidget)
from webhelpers2.html import HTML

View file

@ -25,11 +25,11 @@ Auth Views
"""
import colander
from deform.widget import TextInputWidget, PasswordWidget, CheckedPasswordWidget
from wuttaweb.views import View
from wuttaweb.db import Session
from wuttaweb.auth import login_user, logout_user
from wuttaweb.forms import widgets
class AuthView(View):
@ -97,14 +97,14 @@ class AuthView(View):
schema.add(colander.SchemaNode(
colander.String(),
name='username',
widget=TextInputWidget(attributes={
widget=widgets.TextInputWidget(attributes={
'ref': 'username',
})))
schema.add(colander.SchemaNode(
colander.String(),
name='password',
widget=PasswordWidget(attributes={
widget=widgets.PasswordWidget(attributes={
'ref': 'password',
})))
@ -174,13 +174,13 @@ class AuthView(View):
schema.add(colander.SchemaNode(
colander.String(),
name='current_password',
widget=PasswordWidget(),
widget=widgets.PasswordWidget(),
validator=self.change_password_validate_current_password))
schema.add(colander.SchemaNode(
colander.String(),
name='new_password',
widget=CheckedPasswordWidget(),
widget=widgets.CheckedPasswordWidget(),
validator=self.change_password_validate_new_password))
return schema

View file

@ -28,6 +28,7 @@ import colander
from wuttjamaican.db.model import User
from wuttaweb.views import MasterView
from wuttaweb.forms import widgets
from wuttaweb.forms.schema import PersonRef, RoleRefs
from wuttaweb.db import Session
@ -93,9 +94,15 @@ class UserView(MasterView):
f.set_validator('username', self.unique_username)
# password
# if not (self.creating or self.editing):
# f.remove('password')
# nb. we must avoid 'password' as field name since
# ColanderAlchemy wants to handle the raw/hashed value
f.remove('password')
# nb. no need for password field if readonly
if self.creating or self.editing:
# nb. use 'set_password' as field name
f.append('set_password')
f.set_required('set_password', False)
f.set_widget('set_password', widgets.CheckedPasswordWidget())
# roles
f.append('roles')
@ -121,9 +128,16 @@ class UserView(MasterView):
def objectify(self, form, session=None):
""" """
data = form.validated
# normal logic first
user = super().objectify(form)
# maybe set user password
if 'set_password' in form and data.get('set_password'):
auth = self.app.get_auth_handler()
auth.set_user_password(user, data['set_password'])
# update roles for user
# TODO
# if self.has_perm('edit_roles'):

View file

@ -218,6 +218,18 @@ class TestForm(TestCase):
self.assertIn('name', schema)
self.assertIn('value', schema)
# ColanderAlchemy schema still has *all* requested fields
form = self.make_form(model_instance=model.Setting(name='uhoh'),
fields=['name', 'value', 'foo', 'bar'])
self.assertEqual(form.fields, ['name', 'value', 'foo', 'bar'])
self.assertIsNone(form.schema)
schema = form.get_schema()
self.assertEqual(len(schema.children), 4)
self.assertIn('name', schema)
self.assertIn('value', schema)
self.assertIn('foo', schema)
self.assertIn('bar', schema)
# schema nodes are required by default
form = self.make_form(fields=['foo', 'bar'])
schema = form.get_schema()

View file

@ -15,6 +15,9 @@ class TestUserView(WebTestCase):
def make_view(self):
return mod.UserView(self.request)
def test_includeme(self):
self.pyramid_config.include('wuttaweb.views.users')
def test_get_query(self):
view = self.make_view()
query = view.get_query(session=self.session)
@ -76,11 +79,13 @@ class TestUserView(WebTestCase):
def test_objectify(self):
model = self.app.model
auth = self.app.get_auth_handler()
blokes = model.Role(name="Blokes")
self.session.add(blokes)
others = model.Role(name="Others")
self.session.add(others)
barney = model.User(username='barney')
auth.set_user_password(barney, 'testpass')
barney.roles.append(blokes)
self.session.add(barney)
self.session.commit()
@ -93,6 +98,14 @@ class TestUserView(WebTestCase):
self.assertEqual(len(barney.roles), 1)
self.assertEqual(barney.roles[0].name, "Blokes")
# form can update user password
self.assertTrue(auth.check_user_password(barney, 'testpass'))
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'set_password': 'testpass2'}
user = view.objectify(form, session=self.session)
self.assertIs(user, barney)
self.assertTrue(auth.check_user_password(barney, 'testpass2'))
# form can update user roles
form = view.make_model_form(model_instance=barney)
form.validated = {'username': 'barney', 'roles': {others.uuid}}