mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Require current password when using passwd to change your own password.
Add a new required parameter, current_password. In order to ask this first I added a new parameter option, sortorder. The lower the value the earlier it will be prompted for. I also changed the way autofill works. It will attempt to get the default and if it doesn't get anything will continue prompting interactively. Since current_password is required I'm passing a magic value that means changing someone else's password. We need to pass something since current_password is required. The python-ldap passwd command doesn't seem to use the old password at all so I do a simple bind to validate it. https://fedorahosted.org/freeipa/ticket/1808
This commit is contained in:
parent
651534087c
commit
844d4ff8bf
5
API.txt
5
API.txt
@ -1829,9 +1829,10 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user")
|
||||
command: passwd
|
||||
args: 2,0,3
|
||||
args: 3,0,3
|
||||
arg: Str('principal', validate_principal, autofill=True, cli_name='user', create_default=<lambda>, label=Gettext('User name', domain='ipa', localedir=None), normalizer=<lambda>, primary_key=True)
|
||||
arg: Password('password', label=Gettext('Password', domain='ipa', localedir=None))
|
||||
arg: Password('password', label=Gettext('New Password', domain='ipa', localedir=None))
|
||||
arg: Password('current_password', autofill=True, confirm=False, default_from=<lambda>, label=Gettext('Current Password', domain='ipa', localedir=None), sortorder=-1)
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed')
|
||||
output: Output('result', <type 'bool'>, 'True means the operation was successful')
|
||||
output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user")
|
||||
|
2
VERSION
2
VERSION
@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000
|
||||
# #
|
||||
########################################################
|
||||
IPA_API_VERSION_MAJOR=2
|
||||
IPA_API_VERSION_MINOR=11
|
||||
IPA_API_VERSION_MINOR=12
|
||||
|
@ -1048,12 +1048,14 @@ class cli(backend.Executioner):
|
||||
for param in cmd.params():
|
||||
if (param.required and param.name not in kw) or \
|
||||
(param.alwaysask and honor_alwaysask) or self.env.prompt_all:
|
||||
if param.autofill:
|
||||
kw[param.name] = param.get_default(**kw)
|
||||
if param.name in kw and kw[param.name] is not None:
|
||||
continue
|
||||
if param.password:
|
||||
kw[param.name] = self.Backend.textui.prompt_password(
|
||||
param.label, param.confirm
|
||||
)
|
||||
elif param.autofill:
|
||||
kw[param.name] = param.get_default(**kw)
|
||||
else:
|
||||
default = param.get_default(**kw)
|
||||
error = None
|
||||
|
@ -777,6 +777,8 @@ class Command(HasParam):
|
||||
self._create_param_namespace('options')
|
||||
def get_key(p):
|
||||
if p.required:
|
||||
if p.sortorder < 0:
|
||||
return p.sortorder
|
||||
if p.default_from is None:
|
||||
return 0
|
||||
return 1
|
||||
|
@ -317,6 +317,7 @@ class Param(ReadOnly):
|
||||
('flags', frozenset, frozenset()),
|
||||
('hint', (str, Gettext), None),
|
||||
('alwaysask', bool, False),
|
||||
('sortorder', int, 2), # see finalize()
|
||||
|
||||
# The 'default' kwarg gets appended in Param.__init__():
|
||||
# ('default', self.type, None),
|
||||
|
@ -23,6 +23,7 @@ from ipalib import Str, Password
|
||||
from ipalib import _
|
||||
from ipalib import output
|
||||
from ipalib.plugins.user import split_principal, validate_principal, normalize_principal
|
||||
from ipalib.request import context
|
||||
|
||||
__doc__ = _("""
|
||||
Set a user's password
|
||||
@ -43,6 +44,22 @@ EXAMPLES:
|
||||
ipa passwd tuser1
|
||||
""")
|
||||
|
||||
# We only need to prompt for the current password when changing a password
|
||||
# for yourself, but the parameter is still required
|
||||
MAGIC_VALUE = u'CHANGING_PASSWORD_FOR_ANOTHER_USER'
|
||||
|
||||
def get_current_password(principal):
|
||||
"""
|
||||
If the user is changing their own password then return None so the
|
||||
current password is prompted for, otherwise return a fixed value to
|
||||
be ignored later.
|
||||
"""
|
||||
current_principal = util.get_current_principal()
|
||||
if current_principal == normalize_principal(principal):
|
||||
return None
|
||||
else:
|
||||
return MAGIC_VALUE
|
||||
|
||||
class passwd(Command):
|
||||
__doc__ = _("Set a user's password.")
|
||||
|
||||
@ -56,14 +73,21 @@ class passwd(Command):
|
||||
normalizer=lambda value: normalize_principal(value),
|
||||
),
|
||||
Password('password',
|
||||
label=_('Password'),
|
||||
label=_('New Password'),
|
||||
),
|
||||
Password('current_password',
|
||||
label=_('Current Password'),
|
||||
confirm=False,
|
||||
default_from=lambda principal: get_current_password(principal),
|
||||
autofill=True,
|
||||
sortorder=-1,
|
||||
),
|
||||
)
|
||||
|
||||
has_output = output.standard_value
|
||||
msg_summary = _('Changed password for "%(value)s"')
|
||||
|
||||
def execute(self, principal, password):
|
||||
def execute(self, principal, password, current_password):
|
||||
"""
|
||||
Execute the passwd operation.
|
||||
|
||||
@ -74,6 +98,7 @@ class passwd(Command):
|
||||
|
||||
:param principal: The login name or principal of the user
|
||||
:param password: the new password
|
||||
:param current_password: the existing password, if applicable
|
||||
"""
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
@ -82,7 +107,16 @@ class passwd(Command):
|
||||
",".join([api.env.container_user, api.env.basedn])
|
||||
)
|
||||
|
||||
if principal == getattr(context, 'principal') and \
|
||||
current_password == MAGIC_VALUE:
|
||||
# No cheating
|
||||
self.log.warn('User attempted to change password using magic value')
|
||||
raise errors.ACIError(info='Invalid credentials')
|
||||
|
||||
if current_password == MAGIC_VALUE:
|
||||
ldap.modify_password(dn, password)
|
||||
else:
|
||||
ldap.modify_password(dn, password, current_password)
|
||||
|
||||
return dict(
|
||||
result=True,
|
||||
|
@ -899,6 +899,17 @@ class ldap2(CrudBackend, Encoder):
|
||||
def modify_password(self, dn, new_pass, old_pass=''):
|
||||
"""Set user password."""
|
||||
dn = self.normalize_dn(dn)
|
||||
|
||||
# The python-ldap passwd command doesn't verify the old password
|
||||
# so we'll do a simple bind to validate it.
|
||||
if old_pass != '':
|
||||
try:
|
||||
conn = _ldap.initialize(self.ldap_uri)
|
||||
conn.simple_bind_s(dn, old_pass)
|
||||
conn.unbind()
|
||||
except _ldap.LDAPError, e:
|
||||
_handle_errors(e, **{})
|
||||
|
||||
try:
|
||||
self.conn.passwd_s(dn, old_pass, new_pass)
|
||||
except _ldap.LDAPError, e:
|
||||
|
Loading…
Reference in New Issue
Block a user