Hi,
I have previously commented a number of times on this pattern that you have been using and
that it is incorrect in reviews.
On 26 Mar 2019, at 13:27, Anuj Borah <aborah(a)redhat.com>
wrote:
Hay everyone,
Consider bellow condition:
users = UserAccounts(topo.standalone, f"ou=Keywords,{DEFAULT_SUFFIX}",
rdn=None)
for i in [f'NONE_2_KEY,ou=Authmethod',
f'EVERYDAY_KEY,ou=Dayofweek',
f'TODAY_KEY,ou=Dayofweek',
f'NODAY_KEY,ou=Dayofweek',
f'NETSCAPEDNS_KEY,ou=DNS',
f'FULLIP_KEY,ou=IP',
f'NETSCAPEIP_KEY,ou=IP',
f'NOIP_KEY,ou=IP',
f'FULLWORKER_KEY,ou=Timeofday',
f'DAYWORKER_KEY,ou=Timeofday',
f'NIGHTWORKER_KEY,ou=Timeofday',
f'NOWORKER_KEY,ou=Timeofday']:
properties = {
'uid': i,
'cn': i.split(',')[0],
'sn': 'user',
'uidNumber': '1000',
'gidNumber': '2000',
'homeDirectory': '/home/' + i,
'userPassword': PW_DM
}
users.create(properties=properties)
^ This is where the mistake happens. UserAccounts *already* knows that you have an rdn
etc. So then you say “users.create(uid = “everyday_key,ou=….”).
So it LITERALLY takes that, escapes the content (which is the cause of the \\2C and \\3D
you see below) and put’s that into the field)
If you see the created users:
for i in users.list(): i.dn
'uid=NONE_2_KEY\\2Cou\\3DAuthmethod,ou=Keywords,dc=example,dc=com'
'uid=EVERYDAY_KEY\\2Cou\\3DDayofweek,ou=Keywords,dc=example,dc=com'
'uid=TODAY_KEY\\2Cou\\3DDayofweek,ou=Keywords,dc=example,dc=com'
'uid=NODAY_KEY\\2Cou\\3DDayofweek,ou=Keywords,dc=example,dc=com'
'uid=NETSCAPEDNS_KEY\\2Cou\\3DDNS,ou=Keywords,dc=example,dc=com'
'uid=FULLIP_KEY\\2Cou\\3DIP,ou=Keywords,dc=example,dc=com'
'uid=NETSCAPEIP_KEY\\2Cou\\3DIP,ou=Keywords,dc=example,dc=com'
'uid=NOIP_KEY\\2Cou\\3DIP,ou=Keywords,dc=example,dc=com'
'uid=FULLWORKER_KEY\\2Cou\\3DTimeofday,ou=Keywords,dc=example,dc=com'
'uid=DAYWORKER_KEY\\2Cou\\3DTimeofday,ou=Keywords,dc=example,dc=com'
'uid=NIGHTWORKER_KEY\\2Cou\\3DTimeofday,ou=Keywords,dc=example,dc=com'
'uid=NOWORKER_KEY\\2Cou\\3DTimeofday,ou=Keywords,dc=example,dc=com'
We can see users become useless to use for further operations.
And consider bellow condition:
for i in [f'NONE_2_KEY,ou=Authmethod',
f'EVERYDAY_KEY,ou=Dayofweek',
f'TODAY_KEY,ou=Dayofweek',
f'NODAY_KEY,ou=Dayofweek',
f'NETSCAPEDNS_KEY,ou=DNS',
f'FULLIP_KEY,ou=IP',
f'NETSCAPEIP_KEY,ou=IP',
f'NOIP_KEY,ou=IP',
f'FULLWORKER_KEY,ou=Timeofday',
f'DAYWORKER_KEY,ou=Timeofday',
f'NIGHTWORKER_KEY,ou=Timeofday',
f'NOWORKER_KEY,ou=Timeofday']:
properties = {
'uid': i,
'cn': i.split(',')[0],
'sn': 'user',
'uidNumber': '1000',
'gidNumber': '2000',
'homeDirectory': '/home/' + i,
'userPassword': PW_DM
}
users = UserAccount(topo.standalone,
f"cn={i},ou=Keywords,{DEFAULT_SUFFIX}")
users.create(properties=properties)
users = UserAccounts(topo.standalone, f"ou=Keywords,{DEFAULT_SUFFIX}",
rdn=None)
for i in users.list(): i.dn
'cn=NONE_2_KEY,ou=Authmethod,ou=Keywords,dc=example,dc=com'
'cn=EVERYDAY_KEY,ou=Dayofweek,ou=Keywords,dc=example,dc=com'
'cn=TODAY_KEY,ou=Dayofweek,ou=Keywords,dc=example,dc=com'
'cn=NODAY_KEY,ou=Dayofweek,ou=Keywords,dc=example,dc=com'
'cn=NETSCAPEDNS_KEY,ou=DNS,ou=Keywords,dc=example,dc=com'
'cn=FULLIP_KEY,ou=IP,ou=Keywords,dc=example,dc=com'
'cn=NETSCAPEIP_KEY,ou=IP,ou=Keywords,dc=example,dc=com'
'cn=NOIP_KEY,ou=IP,ou=Keywords,dc=example,dc=com'
'cn=FULLWORKER_KEY,ou=Timeofday,ou=Keywords,dc=example,dc=com'
'cn=DAYWORKER_KEY,ou=Timeofday,ou=Keywords,dc=example,dc=com'
'cn=NIGHTWORKER_KEY,ou=Timeofday,ou=Keywords,dc=example,dc=com'
'cn=NOWORKER_KEY,ou=Timeofday,ou=Keywords,dc=example,dc=com'
Here users are in good shape .
Here, because you do cn as i.split(,), now the cn becomes “EVERYDAY_KEY” and the provided
basedn you have in the UserAccount initialise is now set to the full DN, so the create
works.
Basically, the mistake here is that you are iterating over a list of partial-dn’s and you
expect lib389 to magically fix this all for you, and it’s behaviour is now surprising you
because it’s “making safe” from your mistake.
There are two main pieces of advice I would give here.
First is to *stop* thinking about everything in terms of DN’s and start thinking about
things in terms of “containers and what they hold”. When you do:
uas = UserAccounts(basedn=DEFAULT_SUFFIX, rdn=ou=People)
You are describing “Here is the set of all possible users, and where they exist in the
servers tree”. So now that UserAccounts is setup like this you can do:
uas.create(properties={
uid: ’test'
})
this will be combined to make uid=test + ou=People + DEFAULT_SUFFIX ->
uid=test,ou=people,DEFAULT_SUFFIX.
Now, when we look at your example, because you are trying to take shortcuts, you are
including the location of the object into the cn parameter with things like: ‘ips,ou=ips’
….
There are three possible ways to handle this.
1: (okay, but not the best)
Create each item bypassing UserAccounts, by loading the full dn into the UserAccount
setup. This is done with:
u = UserAccount(topo.standalone, dn=“uid=test,ou=People,dc=example,dc=com”)
u.create(properties={
….
})
Remember, UserAccount (singular) being an absolute object, can have a full path into the
DN like this. Once you have added all these, even if you do say:
u = UserAccount(topo.standalone, dn=“uid=test,ou=Container,ou=People,dc=example,dc=com”)
…
uas = UserAccounts(topo.standalone, DEFAULT_SUFFIX, rdn=ou=People)
uas.list()
uas.list/get will find the userAccount becaus it’s a subtree seacch, regardless of how
many containers exist in the path.
2. (“good enough")
Keep updating and changing your UserAccounts to account for the relative location change
during your create:
authmethod_users = UserAccounts(topo.standalone, DEFAULT_SUFFIX,
rdn=ou=Authmethod,ou=Keywords)
uas.create( …)
ip_users = UserAccounts(topo.standalone, DEFAULT_SUFFIX, rdn=ou=IP,ou=Keywords)
ip_users.create(…)
So this at the very least means you keep things work in the best way possible, even though
you need to do lots of changes to get all the weird rdns. You can probably solve this with
some lists of tuples like:
my users = [ (’name’, ‘ou=ip,ou=keywords’),
….]
for (uid, rdn) in my_users:
us = UserAccounts(topo.standalone, DEFAULT_SUFFIX, rdn)
us.create(properties={ ‘uid’: uid })
3. (best)
Unless the test requires you to have all these crazy ou’s (and even if it does … fixtures
per test, rather than one giant fixture per module …), then your users should all be in a
SINGLE OU anyway. LDAP supports filtering and querying, which is a much more effective way
of expressing constraints and what users you want etc.
So just do:
users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
test_fixture_1
users.create(…)
fin:
for u in users.get():
u.delete()
test_fixture_2
users.create(…)
…
my_test(test_fixture_1)
# Now do the test thing. Apply my ACI to ou=People instead, and do the test, then
reset in the fixture.
I think you often are trying to copy-paste ACI’s as they are from past tests, you are
trying to duplicate bad test structure from TET, and you are then hitting up against
lib389 and wondering why it won’t work. Of course it won’t! Lib389 *can* allow you to do
everything you want with LDAP, but for the common cases, lib389 is trying to guide you to
do things in a certain, correct, and simple way. Sprawling deep tree directories are awful
to administer and understand, where flatter structures defined by group memberships and
filtering are far nicer.
Remember: if you find you are struggling to do something, is it the API that’s at fault?
Or the way you are approaching the problem?
Hope that helps,
—
Sincerely,
William Brown
Senior Software Engineer, 389 Directory Server
SUSE Labs