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('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: 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('homedirectory', attribute=True, cli_name='homedir', 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('telephonenumber', attribute=True, cli_name='phone', multivalue=True, 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: Str('version?', exclude='webui')
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('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: 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('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, query=True, required=False)
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: 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: 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: Str('version?', exclude='webui')
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('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: 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('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)
@ -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('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: 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: Str('version?', exclude='webui')
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('value', <type 'unicode'>, None)
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_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_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_UID "uid"

View File

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

View File

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

View File

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

View File

@ -1,3 +1,13 @@
# Enable the DNA plugin
dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
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
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 ipapython.dn import DN, RDN
DNA_MAGIC = -1
global_output_params = (
Flag('has_password',
label=_('Password'),

View File

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

View File

@ -18,23 +18,25 @@
# You should have received a copy of the GNU General Public License
# 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 posixpath
import os
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 import baseldap
from ipalib.request import context
from ipalib import _, ngettext
from ipalib import output
from ipapython.ipautil import ipa_generate_password
from ipapython.ipavalidate import Email
import posixpath
from ipalib.capabilities import client_has_capability
from ipalib.util import (normalize_sshpubkey, validate_sshpubkey,
convert_sshpubkey_post)
if api.env.in_server and api.env.context in ['lite', 'server']:
from ipaserver.plugins.ldap2 import ldap2
import os
__doc__ = _("""
Users
@ -81,7 +83,6 @@ EXAMPLES:
NO_UPG_MAGIC = '__no_upg__'
DNA_MAGIC = 999
user_output_params = (
Flag('has_keytab',
@ -300,20 +301,16 @@ class user(LDAPObject):
label=_('Random password'),
flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
),
Int('uidnumber',
Int('uidnumber?',
cli_name='uid',
label=_('UID'),
doc=_('User ID Number (system will assign one if not provided)'),
autofill=True,
default=DNA_MAGIC,
minvalue=1,
),
Int('gidnumber',
Int('gidnumber?',
label=_('GID'),
doc=_('Group ID Number'),
minvalue=1,
default=DNA_MAGIC,
autofill=True,
),
Str('street?',
cli_name='street',
@ -468,6 +465,19 @@ class user_add(LDAPCreate):
entry_attrs.setdefault('description', [])
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)
config = ldap.get_ipa_config()[1]
if 'ipamaxusernamelength' in config:
@ -493,7 +503,7 @@ class user_add(LDAPCreate):
api.env.basedn))
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
if not options.get('noprivate', False) and ldap.has_upg():
# User Private Groups - uidNumber == gidNumber

View File

@ -16,7 +16,7 @@ add:homedirectory: /home/tuser
add:loginshell: /bin/bash
add:sn: User
add:uid: tuser
add:uidnumber: 999
add:gidnumber: 999
add:uidnumber: -1
add:gidnumber: -1
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,
),
]