Change DNA magic value to -1 to make UID 999 usable

Change user-add's uid & gid parameters from autofill to optional.
Change the DNA magic value to -1.

For old clients, which will still send 999 when they want DNA
assignment, translate the 999 to -1. This is done via a new
capability, optional_uid_params.

Tests included

https://fedorahosted.org/freeipa/ticket/2886
This commit is contained in:
Petr Viktorin 2013-01-08 04:10:35 -05:00 committed by Martin Kosek
parent 8d432353cc
commit 91606e6679
13 changed files with 144 additions and 28 deletions

13
API.txt
View File

@ -3416,7 +3416,7 @@ option: Str('cn', attribute=True, autofill=True, cli_name='cn', multivalue=False
option: Str('displayname', attribute=True, autofill=True, cli_name='displayname', multivalue=False, required=False) option: Str('displayname', attribute=True, autofill=True, cli_name='displayname', multivalue=False, required=False)
option: Str('facsimiletelephonenumber', attribute=True, cli_name='fax', multivalue=True, required=False) option: Str('facsimiletelephonenumber', attribute=True, cli_name='fax', multivalue=True, required=False)
option: Str('gecos', attribute=True, autofill=True, cli_name='gecos', multivalue=False, required=False) option: Str('gecos', attribute=True, autofill=True, cli_name='gecos', multivalue=False, required=False)
option: Int('gidnumber', attribute=True, autofill=True, cli_name='gidnumber', default=999, minvalue=1, multivalue=False, required=True) option: Int('gidnumber', attribute=True, cli_name='gidnumber', minvalue=1, multivalue=False, required=False)
option: Str('givenname', attribute=True, cli_name='first', multivalue=False, required=True) option: Str('givenname', attribute=True, cli_name='first', multivalue=False, required=True)
option: Str('homedirectory', attribute=True, cli_name='homedir', multivalue=False, required=False) option: Str('homedirectory', attribute=True, cli_name='homedir', multivalue=False, required=False)
option: Str('initials', attribute=True, autofill=True, cli_name='initials', multivalue=False, required=False) option: Str('initials', attribute=True, autofill=True, cli_name='initials', multivalue=False, required=False)
@ -3440,7 +3440,7 @@ option: Str('st', attribute=True, cli_name='state', multivalue=False, required=F
option: Str('street', attribute=True, cli_name='street', multivalue=False, required=False) option: Str('street', attribute=True, cli_name='street', multivalue=False, required=False)
option: Str('telephonenumber', attribute=True, cli_name='phone', multivalue=True, required=False) option: Str('telephonenumber', attribute=True, cli_name='phone', multivalue=True, required=False)
option: Str('title', attribute=True, cli_name='title', multivalue=False, required=False) option: Str('title', attribute=True, cli_name='title', multivalue=False, required=False)
option: Int('uidnumber', attribute=True, autofill=True, cli_name='uid', default=999, minvalue=1, multivalue=False, required=True) option: Int('uidnumber', attribute=True, cli_name='uid', minvalue=1, multivalue=False, required=False)
option: Password('userpassword', attribute=True, cli_name='password', exclude='webui', multivalue=False, required=False) option: Password('userpassword', attribute=True, cli_name='password', exclude='webui', multivalue=False, required=False)
option: Str('version?', exclude='webui') option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
@ -3477,7 +3477,7 @@ option: Str('cn', attribute=True, autofill=False, cli_name='cn', multivalue=Fals
option: Str('displayname', attribute=True, autofill=False, cli_name='displayname', multivalue=False, query=True, required=False) option: Str('displayname', attribute=True, autofill=False, cli_name='displayname', multivalue=False, query=True, required=False)
option: Str('facsimiletelephonenumber', attribute=True, autofill=False, cli_name='fax', multivalue=True, query=True, required=False) option: Str('facsimiletelephonenumber', attribute=True, autofill=False, cli_name='fax', multivalue=True, query=True, required=False)
option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', multivalue=False, query=True, required=False) option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', multivalue=False, query=True, required=False)
option: Int('gidnumber', attribute=True, autofill=False, cli_name='gidnumber', default=999, minvalue=1, multivalue=False, query=True, required=False) option: Int('gidnumber', attribute=True, autofill=False, cli_name='gidnumber', minvalue=1, multivalue=False, query=True, required=False)
option: Str('givenname', attribute=True, autofill=False, cli_name='first', multivalue=False, query=True, required=False) option: Str('givenname', attribute=True, autofill=False, cli_name='first', multivalue=False, query=True, required=False)
option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, query=True, required=False) option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, query=True, required=False)
option: Str('in_group*', cli_name='in_groups', csv=True) option: Str('in_group*', cli_name='in_groups', csv=True)
@ -3511,7 +3511,7 @@ option: Str('telephonenumber', attribute=True, autofill=False, cli_name='phone',
option: Int('timelimit?', autofill=False, minvalue=0) option: Int('timelimit?', autofill=False, minvalue=0)
option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, query=True, required=False) option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, query=True, required=False)
option: Str('uid', attribute=True, autofill=False, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, query=True, required=False) option: Str('uid', attribute=True, autofill=False, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, query=True, required=False)
option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', default=999, minvalue=1, multivalue=False, query=True, required=False) option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', minvalue=1, multivalue=False, query=True, required=False)
option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', multivalue=False, query=True, required=False) option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', multivalue=False, query=True, required=False)
option: Str('version?', exclude='webui') option: Str('version?', exclude='webui')
option: Flag('whoami', autofill=True, default=False) option: Flag('whoami', autofill=True, default=False)
@ -3530,7 +3530,7 @@ option: Str('delattr*', cli_name='delattr', exclude='webui')
option: Str('displayname', attribute=True, autofill=False, cli_name='displayname', multivalue=False, required=False) option: Str('displayname', attribute=True, autofill=False, cli_name='displayname', multivalue=False, required=False)
option: Str('facsimiletelephonenumber', attribute=True, autofill=False, cli_name='fax', multivalue=True, required=False) option: Str('facsimiletelephonenumber', attribute=True, autofill=False, cli_name='fax', multivalue=True, required=False)
option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', multivalue=False, required=False) option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', multivalue=False, required=False)
option: Int('gidnumber', attribute=True, autofill=False, cli_name='gidnumber', default=999, minvalue=1, multivalue=False, required=False) option: Int('gidnumber', attribute=True, autofill=False, cli_name='gidnumber', minvalue=1, multivalue=False, required=False)
option: Str('givenname', attribute=True, autofill=False, cli_name='first', multivalue=False, required=False) option: Str('givenname', attribute=True, autofill=False, cli_name='first', multivalue=False, required=False)
option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, required=False) option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, required=False)
option: Str('initials', attribute=True, autofill=False, cli_name='initials', multivalue=False, required=False) option: Str('initials', attribute=True, autofill=False, cli_name='initials', multivalue=False, required=False)
@ -3554,7 +3554,7 @@ option: Str('st', attribute=True, autofill=False, cli_name='state', multivalue=F
option: Str('street', attribute=True, autofill=False, cli_name='street', multivalue=False, required=False) option: Str('street', attribute=True, autofill=False, cli_name='street', multivalue=False, required=False)
option: Str('telephonenumber', attribute=True, autofill=False, cli_name='phone', multivalue=True, required=False) option: Str('telephonenumber', attribute=True, autofill=False, cli_name='phone', multivalue=True, required=False)
option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, required=False) option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, required=False)
option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', default=999, minvalue=1, multivalue=False, required=False) option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', minvalue=1, multivalue=False, required=False)
option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', multivalue=False, required=False) option: Password('userpassword', attribute=True, autofill=False, cli_name='password', exclude='webui', multivalue=False, required=False)
option: Str('version?', exclude='webui') option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
@ -3588,3 +3588,4 @@ output: Output('result', <type 'bool'>, None)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None) output: Output('value', <type 'unicode'>, None)
capability: messages 2.52 capability: messages 2.52
capability: optional_uid_params 2.54

View File

@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
# # # #
######################################################## ########################################################
IPA_API_VERSION_MAJOR=2 IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=53 IPA_API_VERSION_MINOR=54

View File

@ -101,7 +101,7 @@ bool secrets_store(const char *key, const void *data, size_t size); /* available
#define IPA_KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1" #define IPA_KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1"
#define IPA_KEYTAB_SET_OID_OLD "2.16.840.1.113730.3.8.3.1" #define IPA_KEYTAB_SET_OID_OLD "2.16.840.1.113730.3.8.3.1"
#define IPA_MAGIC_ID_STR "999" #define IPA_MAGIC_ID_STR "-1"
#define LDAP_ATTRIBUTE_CN "cn" #define LDAP_ATTRIBUTE_CN "cn"
#define LDAP_ATTRIBUTE_UID "uid" #define LDAP_ATTRIBUTE_UID "uid"

View File

@ -24,5 +24,5 @@ ipaWinSyncDefaultGroupAttr: ipaDefaultPrimaryGroup
ipaWinSyncDefaultGroupFilter: (gidNumber=*)(objectclass=posixGroup)(objectclass=groupOfNames) ipaWinSyncDefaultGroupFilter: (gidNumber=*)(objectclass=posixGroup)(objectclass=groupOfNames)
ipaWinSyncAcctDisable: both ipaWinSyncAcctDisable: both
ipaWinSyncForceSync: true ipaWinSyncForceSync: true
ipaWinSyncUserAttr: uidNumber 999 ipaWinSyncUserAttr: uidNumber -1
ipaWinSyncUserAttr: gidNumber 999 ipaWinSyncUserAttr: gidNumber -1

View File

@ -2,7 +2,7 @@ dn: cn=Default SMB Group,cn=groups,cn=accounts,$SUFFIX
changetype: add changetype: add
cn: Default SMB Group cn: Default SMB Group
description: Fallback group for primary group RID, do not add users to this group description: Fallback group for primary group RID, do not add users to this group
gidnumber: 999 gidnumber: -1
objectclass: top objectclass: top
objectclass: ipaobject objectclass: ipaobject
objectclass: posixgroup objectclass: posixgroup

View File

@ -9,7 +9,7 @@ dnaType: uidNumber
dnaType: gidNumber dnaType: gidNumber
dnaNextValue: eval($IDSTART) dnaNextValue: eval($IDSTART)
dnaMaxValue: eval($IDMAX) dnaMaxValue: eval($IDMAX)
dnaMagicRegen: 999 dnaMagicRegen: -1
dnaFilter: (|(objectClass=posixAccount)(objectClass=posixGroup)(objectClass=ipaIDobject)) dnaFilter: (|(objectClass=posixAccount)(objectClass=posixGroup)(objectClass=ipaIDobject))
dnaScope: $SUFFIX dnaScope: $SUFFIX
dnaThreshold: 500 dnaThreshold: 500

View File

@ -1,3 +1,13 @@
# Enable the DNA plugin # Enable the DNA plugin
dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
only:nsslapd-pluginEnabled: on only:nsslapd-pluginEnabled: on
# Change the magic value to -1
dn: cn=Posix IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
only:dnaMagicRegen: -1
dn: cn=ipa-winsync,cn=plugins,cn=config
remove:ipaWinSyncUserAttr: uidNumber 999
remove:ipaWinSyncUserAttr: gidNumber 999
add:ipaWinSyncUserAttr: uidNumber -1
add:ipaWinSyncUserAttr: gidNumber -1

View File

@ -35,6 +35,12 @@ capabilities = dict(
# http://freeipa.org/page/V3/Messages # http://freeipa.org/page/V3/Messages
messages=u'2.52', messages=u'2.52',
# optional_uid_params: Before this version, UID & GID parameter defaults
# were 999, which meant "assign dynamically", so was not possible to get
# a user with UID=999. With the capability, these parameters are optional
# and 999 really means 999.
# https://fedorahosted.org/freeipa/ticket/2886
optional_uid_params=u'2.54'
) )

View File

@ -34,6 +34,8 @@ from ipalib.text import _
from ipalib.util import json_serialize, validate_hostname from ipalib.util import json_serialize, validate_hostname
from ipapython.dn import DN, RDN from ipapython.dn import DN, RDN
DNA_MAGIC = -1
global_output_params = ( global_output_params = (
Flag('has_password', Flag('has_password',
label=_('Password'), label=_('Password'),

View File

@ -21,6 +21,7 @@
from ipalib import api from ipalib import api
from ipalib import Int, Str from ipalib import Int, Str
from ipalib.plugins.baseldap import * from ipalib.plugins.baseldap import *
from ipalib.plugins import baseldap
from ipalib import _, ngettext from ipalib import _, ngettext
if api.env.in_server and api.env.context in ['lite', 'server']: if api.env.in_server and api.env.context in ['lite', 'server']:
try: try:
@ -202,7 +203,7 @@ class group_add(LDAPCreate):
elif not options['nonposix']: elif not options['nonposix']:
entry_attrs['objectclass'].append('posixgroup') entry_attrs['objectclass'].append('posixgroup')
if not 'gidnumber' in options: if not 'gidnumber' in options:
entry_attrs['gidnumber'] = 999 entry_attrs['gidnumber'] = baseldap.DNA_MAGIC
return dn return dn
@ -281,7 +282,7 @@ class group_mod(LDAPUpdate):
old_entry_attrs['objectclass'].append('posixgroup') old_entry_attrs['objectclass'].append('posixgroup')
entry_attrs['objectclass'] = old_entry_attrs['objectclass'] entry_attrs['objectclass'] = old_entry_attrs['objectclass']
if not 'gidnumber' in options: if not 'gidnumber' in options:
entry_attrs['gidnumber'] = 999 entry_attrs['gidnumber'] = baseldap.DNA_MAGIC
if options['external']: if options['external']:
if is_protected_group: if is_protected_group:

View File

@ -18,23 +18,25 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from time import gmtime, strftime, strptime from time import gmtime, strftime
import string import string
import posixpath
import os
from ipalib import api, errors from ipalib import api, errors
from ipalib import Flag, Int, Password, Str, Bool, Bytes from ipalib import Flag, Int, Password, Str, Bool
from ipalib.plugins.baseldap import * from ipalib.plugins.baseldap import *
from ipalib.plugins import baseldap
from ipalib.request import context from ipalib.request import context
from ipalib import _, ngettext from ipalib import _, ngettext
from ipalib import output from ipalib import output
from ipapython.ipautil import ipa_generate_password from ipapython.ipautil import ipa_generate_password
from ipapython.ipavalidate import Email from ipapython.ipavalidate import Email
import posixpath from ipalib.capabilities import client_has_capability
from ipalib.util import (normalize_sshpubkey, validate_sshpubkey, from ipalib.util import (normalize_sshpubkey, validate_sshpubkey,
convert_sshpubkey_post) convert_sshpubkey_post)
if api.env.in_server and api.env.context in ['lite', 'server']: if api.env.in_server and api.env.context in ['lite', 'server']:
from ipaserver.plugins.ldap2 import ldap2 from ipaserver.plugins.ldap2 import ldap2
import os
__doc__ = _(""" __doc__ = _("""
Users Users
@ -81,7 +83,6 @@ EXAMPLES:
NO_UPG_MAGIC = '__no_upg__' NO_UPG_MAGIC = '__no_upg__'
DNA_MAGIC = 999
user_output_params = ( user_output_params = (
Flag('has_keytab', Flag('has_keytab',
@ -300,20 +301,16 @@ class user(LDAPObject):
label=_('Random password'), label=_('Random password'),
flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'), flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
), ),
Int('uidnumber', Int('uidnumber?',
cli_name='uid', cli_name='uid',
label=_('UID'), label=_('UID'),
doc=_('User ID Number (system will assign one if not provided)'), doc=_('User ID Number (system will assign one if not provided)'),
autofill=True,
default=DNA_MAGIC,
minvalue=1, minvalue=1,
), ),
Int('gidnumber', Int('gidnumber?',
label=_('GID'), label=_('GID'),
doc=_('Group ID Number'), doc=_('Group ID Number'),
minvalue=1, minvalue=1,
default=DNA_MAGIC,
autofill=True,
), ),
Str('street?', Str('street?',
cli_name='street', cli_name='street',
@ -468,6 +465,19 @@ class user_add(LDAPCreate):
entry_attrs.setdefault('description', []) entry_attrs.setdefault('description', [])
entry_attrs['description'].append(NO_UPG_MAGIC) entry_attrs['description'].append(NO_UPG_MAGIC)
entry_attrs.setdefault('uidnumber', baseldap.DNA_MAGIC)
if not client_has_capability(
options['version'], 'optional_uid_params'):
# https://fedorahosted.org/freeipa/ticket/2886
# Old clients say 999 (OLD_DNA_MAGIC) when they really mean
# "assign a value dynamically".
OLD_DNA_MAGIC = 999
if entry_attrs.get('uidnumber') == OLD_DNA_MAGIC:
entry_attrs['uidnumber'] = baseldap.DNA_MAGIC
if entry_attrs.get('gidnumber') == OLD_DNA_MAGIC:
entry_attrs['gidnumber'] = baseldap.DNA_MAGIC
validate_nsaccountlock(entry_attrs) validate_nsaccountlock(entry_attrs)
config = ldap.get_ipa_config()[1] config = ldap.get_ipa_config()[1]
if 'ipamaxusernamelength' in config: if 'ipamaxusernamelength' in config:
@ -493,7 +503,7 @@ class user_add(LDAPCreate):
api.env.basedn)) api.env.basedn))
entry_attrs.setdefault('krbprincipalname', '%s@%s' % (entry_attrs['uid'], api.env.realm)) entry_attrs.setdefault('krbprincipalname', '%s@%s' % (entry_attrs['uid'], api.env.realm))
if entry_attrs.get('gidnumber', DNA_MAGIC) == DNA_MAGIC: if entry_attrs.get('gidnumber') is None:
# gidNumber wasn't specified explicity, find out what it should be # gidNumber wasn't specified explicity, find out what it should be
if not options.get('noprivate', False) and ldap.has_upg(): if not options.get('noprivate', False) and ldap.has_upg():
# User Private Groups - uidNumber == gidNumber # User Private Groups - uidNumber == gidNumber

View File

@ -16,7 +16,7 @@ add:homedirectory: /home/tuser
add:loginshell: /bin/bash add:loginshell: /bin/bash
add:sn: User add:sn: User
add:uid: tuser add:uid: tuser
add:uidnumber: 999 add:uidnumber: -1
add:gidnumber: 999 add:gidnumber: -1
add:cn: Test User add:cn: Test User

View File

@ -1748,4 +1748,90 @@ class test_user(Declarative):
), ),
), ),
), ),
dict(
desc='Create "%s" with UID 999' % user1,
command=(
'user_add', [user1], dict(
givenname=u'Test', sn=u'User1', uidnumber=999)
),
expected=dict(
value=user1,
summary=u'Added user "%s"' % user1,
result=dict(
gecos=[u'Test User1'],
givenname=[u'Test'],
homedirectory=[u'/home/tuser1'],
krbprincipalname=[u'tuser1@' + api.env.realm],
loginshell=[u'/bin/sh'],
objectclass=objectclasses.user,
sn=[u'User1'],
uid=[user1],
uidnumber=[u'999'],
gidnumber=[u'999'],
displayname=[u'Test User1'],
cn=[u'Test User1'],
mail=[u'%s@%s' % (user1, api.env.domain)],
initials=[u'TU'],
ipauniqueid=[fuzzy_uuid],
krbpwdpolicyreference=[DN(('cn','global_policy'),('cn',api.env.realm),
('cn','kerberos'),api.env.basedn)],
mepmanagedentry=[get_group_dn(user1)],
memberof_group=[u'ipausers'],
has_keytab=False,
has_password=False,
dn=get_user_dn(user1),
),
),
extra_check = upg_check,
),
dict(
desc='Delete "%s"' % user1,
command=('user_del', [user1], {}),
expected=dict(
result=dict(failed=u''),
summary=u'Deleted user "%s"' % user1,
value=user1,
),
),
dict(
desc='Create "%s" with old DNA_MAGIC uid 999' % user1,
command=(
'user_add', [user1], dict(
givenname=u'Test', sn=u'User1', uidnumber=999,
version=u'2.49')
),
expected=dict(
value=user1,
summary=u'Added user "%s"' % user1,
result=dict(
gecos=[u'Test User1'],
givenname=[u'Test'],
homedirectory=[u'/home/tuser1'],
krbprincipalname=[u'tuser1@' + api.env.realm],
loginshell=[u'/bin/sh'],
objectclass=objectclasses.user,
sn=[u'User1'],
uid=[user1],
uidnumber=[lambda v: int(v) != 999],
gidnumber=[lambda v: int(v) != 999],
displayname=[u'Test User1'],
cn=[u'Test User1'],
mail=[u'%s@%s' % (user1, api.env.domain)],
initials=[u'TU'],
ipauniqueid=[fuzzy_uuid],
krbpwdpolicyreference=[DN(('cn','global_policy'),('cn',api.env.realm),
('cn','kerberos'),api.env.basedn)],
mepmanagedentry=[get_group_dn(user1)],
memberof_group=[u'ipausers'],
has_keytab=False,
has_password=False,
dn=get_user_dn(user1),
),
),
extra_check = upg_check,
),
] ]