feat: expose User password for editing in master views
This commit is contained in:
parent
230e2fd1ab
commit
330ee324ba
|
@ -674,6 +674,13 @@ class Form:
|
||||||
schema = SQLAlchemySchemaNode(self.model_class,
|
schema = SQLAlchemySchemaNode(self.model_class,
|
||||||
includes=includes)
|
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:
|
else:
|
||||||
|
|
||||||
# make basic schema
|
# make basic schema
|
||||||
|
|
|
@ -31,12 +31,15 @@ in the namespace:
|
||||||
* :class:`deform:deform.widget.Widget` (base class)
|
* :class:`deform:deform.widget.Widget` (base class)
|
||||||
* :class:`deform:deform.widget.TextInputWidget`
|
* :class:`deform:deform.widget.TextInputWidget`
|
||||||
* :class:`deform:deform.widget.TextAreaWidget`
|
* :class:`deform:deform.widget.TextAreaWidget`
|
||||||
|
* :class:`deform:deform.widget.PasswordWidget`
|
||||||
|
* :class:`deform:deform.widget.CheckedPasswordWidget`
|
||||||
* :class:`deform:deform.widget.SelectWidget`
|
* :class:`deform:deform.widget.SelectWidget`
|
||||||
* :class:`deform:deform.widget.CheckboxChoiceWidget`
|
* :class:`deform:deform.widget.CheckboxChoiceWidget`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
|
from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
|
||||||
|
PasswordWidget, CheckedPasswordWidget,
|
||||||
SelectWidget, CheckboxChoiceWidget)
|
SelectWidget, CheckboxChoiceWidget)
|
||||||
from webhelpers2.html import HTML
|
from webhelpers2.html import HTML
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,11 @@ Auth Views
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
from deform.widget import TextInputWidget, PasswordWidget, CheckedPasswordWidget
|
|
||||||
|
|
||||||
from wuttaweb.views import View
|
from wuttaweb.views import View
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
from wuttaweb.auth import login_user, logout_user
|
from wuttaweb.auth import login_user, logout_user
|
||||||
|
from wuttaweb.forms import widgets
|
||||||
|
|
||||||
|
|
||||||
class AuthView(View):
|
class AuthView(View):
|
||||||
|
@ -97,14 +97,14 @@ class AuthView(View):
|
||||||
schema.add(colander.SchemaNode(
|
schema.add(colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
name='username',
|
name='username',
|
||||||
widget=TextInputWidget(attributes={
|
widget=widgets.TextInputWidget(attributes={
|
||||||
'ref': 'username',
|
'ref': 'username',
|
||||||
})))
|
})))
|
||||||
|
|
||||||
schema.add(colander.SchemaNode(
|
schema.add(colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
name='password',
|
name='password',
|
||||||
widget=PasswordWidget(attributes={
|
widget=widgets.PasswordWidget(attributes={
|
||||||
'ref': 'password',
|
'ref': 'password',
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
@ -174,13 +174,13 @@ class AuthView(View):
|
||||||
schema.add(colander.SchemaNode(
|
schema.add(colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
name='current_password',
|
name='current_password',
|
||||||
widget=PasswordWidget(),
|
widget=widgets.PasswordWidget(),
|
||||||
validator=self.change_password_validate_current_password))
|
validator=self.change_password_validate_current_password))
|
||||||
|
|
||||||
schema.add(colander.SchemaNode(
|
schema.add(colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
name='new_password',
|
name='new_password',
|
||||||
widget=CheckedPasswordWidget(),
|
widget=widgets.CheckedPasswordWidget(),
|
||||||
validator=self.change_password_validate_new_password))
|
validator=self.change_password_validate_new_password))
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
|
|
|
@ -28,6 +28,7 @@ import colander
|
||||||
|
|
||||||
from wuttjamaican.db.model import User
|
from wuttjamaican.db.model import User
|
||||||
from wuttaweb.views import MasterView
|
from wuttaweb.views import MasterView
|
||||||
|
from wuttaweb.forms import widgets
|
||||||
from wuttaweb.forms.schema import PersonRef, RoleRefs
|
from wuttaweb.forms.schema import PersonRef, RoleRefs
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
|
|
||||||
|
@ -93,9 +94,15 @@ class UserView(MasterView):
|
||||||
f.set_validator('username', self.unique_username)
|
f.set_validator('username', self.unique_username)
|
||||||
|
|
||||||
# password
|
# password
|
||||||
# if not (self.creating or self.editing):
|
# nb. we must avoid 'password' as field name since
|
||||||
# f.remove('password')
|
# ColanderAlchemy wants to handle the raw/hashed value
|
||||||
f.remove('password')
|
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
|
# roles
|
||||||
f.append('roles')
|
f.append('roles')
|
||||||
|
@ -121,9 +128,16 @@ class UserView(MasterView):
|
||||||
|
|
||||||
def objectify(self, form, session=None):
|
def objectify(self, form, session=None):
|
||||||
""" """
|
""" """
|
||||||
|
data = form.validated
|
||||||
|
|
||||||
# normal logic first
|
# normal logic first
|
||||||
user = super().objectify(form)
|
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
|
# update roles for user
|
||||||
# TODO
|
# TODO
|
||||||
# if self.has_perm('edit_roles'):
|
# if self.has_perm('edit_roles'):
|
||||||
|
|
|
@ -218,6 +218,18 @@ class TestForm(TestCase):
|
||||||
self.assertIn('name', schema)
|
self.assertIn('name', schema)
|
||||||
self.assertIn('value', 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
|
# schema nodes are required by default
|
||||||
form = self.make_form(fields=['foo', 'bar'])
|
form = self.make_form(fields=['foo', 'bar'])
|
||||||
schema = form.get_schema()
|
schema = form.get_schema()
|
||||||
|
|
|
@ -15,6 +15,9 @@ class TestUserView(WebTestCase):
|
||||||
def make_view(self):
|
def make_view(self):
|
||||||
return mod.UserView(self.request)
|
return mod.UserView(self.request)
|
||||||
|
|
||||||
|
def test_includeme(self):
|
||||||
|
self.pyramid_config.include('wuttaweb.views.users')
|
||||||
|
|
||||||
def test_get_query(self):
|
def test_get_query(self):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
query = view.get_query(session=self.session)
|
query = view.get_query(session=self.session)
|
||||||
|
@ -76,11 +79,13 @@ class TestUserView(WebTestCase):
|
||||||
|
|
||||||
def test_objectify(self):
|
def test_objectify(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
auth = self.app.get_auth_handler()
|
||||||
blokes = model.Role(name="Blokes")
|
blokes = model.Role(name="Blokes")
|
||||||
self.session.add(blokes)
|
self.session.add(blokes)
|
||||||
others = model.Role(name="Others")
|
others = model.Role(name="Others")
|
||||||
self.session.add(others)
|
self.session.add(others)
|
||||||
barney = model.User(username='barney')
|
barney = model.User(username='barney')
|
||||||
|
auth.set_user_password(barney, 'testpass')
|
||||||
barney.roles.append(blokes)
|
barney.roles.append(blokes)
|
||||||
self.session.add(barney)
|
self.session.add(barney)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
@ -93,6 +98,14 @@ class TestUserView(WebTestCase):
|
||||||
self.assertEqual(len(barney.roles), 1)
|
self.assertEqual(len(barney.roles), 1)
|
||||||
self.assertEqual(barney.roles[0].name, "Blokes")
|
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 can update user roles
|
||||||
form = view.make_model_form(model_instance=barney)
|
form = view.make_model_form(model_instance=barney)
|
||||||
form.validated = {'username': 'barney', 'roles': {others.uuid}}
|
form.validated = {'username': 'barney', 'roles': {others.uuid}}
|
||||||
|
|
Loading…
Reference in a new issue