If you delete an user or group in windows AD, it will in "DElETE objects".
I want to use python ldap lib to get them.
Code:
<code>
import ldap
uri = "ldap://10.64.74.17"
user = "XXXXXXXXXX"
password = "XXXXXXXXXXXX"
ldap.set_option(ldap.OPT_REFERRALS, 0)
ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 5)
ldap.protocol_version = 3
ldapClient = ldap.initialize(uri)
ldapClient.simple_bind_s(user, password)
filter = "(&(objectclass=person)(isDeleted=true)(!(objectclass=computer)))"
results = ldapClient.search_s("DC=xx,DC=com", ldap.SCOPE_SUBTREE,filter)
for result in results:
print result
ldapClient.unbind_s()
</code>
It can't show deleted objects.
What's wrong with this code?
You need to add an ldap control to your search : create the request control for the particular operation, and then pass a collection of controls to your search request as an optional parameter.
In your case, this OID for AD is 1.2.840.113556.1.4.417.
LDAP_SERVER_SHOW_DELETED_OID : 1.2.840.113556.1.4.417
Used with an LDAP operation to specify that tombstones and deleted-objects are visible to the operation.
tombstone_control = ('1.2.840.113556.1.4.417',criticality=1)
results = ldapClient.search_s("DC=xx,DC=com", ldap.SCOPE_SUBTREE,filter, [tombstone_control])
You can also scope your search base to CN=Deleted Objects, DC=xx,DC=com as this is where all deleted objects end up. You should make sure your deleted objects are there first. You can use ldp.exe to check.
Related
I am able successfully connect using LDAP3 and retrieve my LDAP group members as below.
from ldap3 import Server, Connection, ALL, SUBTREE
from ldap3.extend.microsoft.addMembersToGroups import ad_add_members_to_groups as addMembersToGroups
>>> conn = Connection(Server('ldaps://ldap.****.com:***', get_info=ALL),check_names=False, auto_bind=False,user="ANT\*****",password="******", authentication="NTLM")
>>>
>>> conn.open()
>>> conn.search('ou=Groups,o=****.com', '(&(cn=MY-LDAP-GROUP))', attributes=['cn', 'objectclass', 'memberuid'])
it returns True and I can see members by printing
conn.entries
>>>
The above line says MY-LDAP-GROUP exists and returns TRUE while searching but throws LDAP group not found when I try to an user to the group as below
>>> addMembersToGroups(conn, ['myuser'], 'MY-LDAP-GROUP')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/****/anaconda3/lib/python3.7/site-packages/ldap3/extend/microsoft/addMembersToGroups.py", line 69, in ad_add_members_to_groups
raise LDAPInvalidDnError(group + ' not found')
ldap3.core.exceptions.LDAPInvalidDnError: MY-LDAP-GROUP not found
>>>
The above line says MY-LDAP-GROUP exists and returns TRUE
Returning True just means that the search succeeded. It doesn't mean that anything was found. Is there anything in conn.entries?
But I suspect your real problem is something different. If this is the source code for ad_add_members_to_groups, then it is expecting the distinguishedName of the group (notice the parameter name group_dn), but you're passing the cn (common name). For example, your code should be something like:
addMembersToGroups(conn, ['myuser'], 'CN=MY-LDAP-GROUP,OU=Groups,DC=example,DC=com')
If you don't know the DN, then ask for the distinguishedName attribute from the search.
A word of warning: that code for ad_add_members_to_groups retrieves all the current members before adding the new member. You might run into performance problems if you're working with groups that have large membership because of that (e.g. if the group has 1000 members, it will load all 1000 before adding anyone). You don't actually need to do that (you can add a new member without looking at the current membership). I think what they're trying to avoid is the error you get when you try to add someone who is already in the group. But I think there are better ways to handle that. It might not matter to you if you're only working with small groups.
After so many trial and errors, I got frustrated and used the older python-ldap library to add existing users. Now my code is a mixture of ldap3 and ldap.
I know this is not what the OP has desired. But this may help someone.
Here the user Dinesh Kumar is already part of a group group1. I am trying to add him
to another group group2 which is successful and does not disturb the existing group
import ldap
import ldap.modlist as modlist
def add_existing_user_to_group(user_name, user_id, group_id):
"""
:return:
"""
# ldap expects a byte string.
converted_user_name = bytes(user_name, 'utf-8')
converted_user_id = bytes(user_id, 'utf-8')
converted_group_id = bytes(group_id, 'utf-8')
# Add all the attributes for the new dn
ldap_attr = {}
ldap_attr['uid'] = converted_user_name
ldap_attr['cn'] = converted_user_name
ldap_attr['uidNumber'] = converted_user_id
ldap_attr['gidNumber'] = converted_group_id
ldap_attr['objectClass'] = [b'top', b'posixAccount', b'inetOrgPerson']
ldap_attr['sn'] = b'Kumar'
ldap_attr['homeDirectory'] = b'/home/users/dkumar'
# Establish connection to server using ldap
conn = ldap.initialize(server_uri, bytes_mode=False)
bind_resp = conn.simple_bind_s("cn=admin,dc=testldap,dc=com", "password")
dn_new = "cn={},cn={},ou=MyOU,dc=testldap,dc=com".format('Dinesh Kumar','group2')
ldif = modlist.addModlist(ldap_attr)
try:
response = conn.add_s(dn_new, ldif)
except ldap.error as e:
response = e
print(" The response is ", response)
conn.unbind()
return response
To preface, I've only been working with Python for about 5 months. I've been trying to write a program that will (eventually) do batch user creation. When formatted like it is below, it will successfully create a new user object, but the "userAccountControl" attribute will default to 546, ACCOUNTDISABLE | PASSWD_NOTREQD | NORMAL_ACCOUNT, and the value for "userPass" will appear plaintext as an octet string in the attributes editor for the object in AD. This program is using the ldap3 library: https://pypi.python.org/pypi/ldap3
class fromconfig:
def __init__(self):
Config = configparser.ConfigParser()
Config.read("config.ini")
self.serverip = Config.get('serverinfo', 'ip')
self.basepath = Config.get('serverinfo', 'base')
self.container = Config.get('serverinfo', 'container')
self.dc1 = Config.get('serverinfo', 'dc1')
self.dc2 = Config.get('serverinfo', 'dc2')
self.ou = Config.get('serverinfo', 'ou')
def add_user(username, givenname, surname, userPrincipalName, SAMAccountName, userPassword):
ad_server = Server(config.serverip, use_ssl=True, get_info=ALL)
ad_c = Connection(ad_server, user='domain\\user', password='password', authentication=NTLM)
if ad_c.bind():
ad_c.add('cn={},cn={},dc={},dc={}'.format(username, config.ou, config.dc1, config.dc2), ['person', 'user'], {'givenName': givenname, 'sn': surname, 'userPrincipalName': userPrincipalName, 'sAMAccountName': SAMAccountName, 'userPassword': userPassword})
print(ad_c.result)
ad_c.unbind()
I want to be able to define a 512 value for userAccountControl in the program, or otherwise successfully enable the account so that I don't have to go back through and uncheck "Account is disabled" in AD later. When I try to pass it, ad_c.result returns with an error 53. It's the same error I receive when I go in through AD and try to modify the attribute directly, or uncheck the disable account checkbox. The dialog for error 53 on the AD server says "The password does not meet length or complexity requirements", but the password I'm using for my testing is one that I've used on our AD in the past with no problems. So I think the issue has something to do with how userPassword is being stored rather than complexity or permissions.
I believe I've figured it out, thanks to the example from this. Defining a value for the "userPassword" key in the attributes dictionary will not work later when you go to manually enable the account. Instead, after adding the user account you can use the extended operations to unlock the account, modify the password, and then update the "userAccountControl" attribute with the desired value (In this case, I wanted 512: NORMAL_ACCOUNT).
It's extremely messy, but thankfully it works, so I'm now going to start refactoring the rest of the program so it doesn't look hideous
(continued from above)
ad_c.add(...)
ad_c.extend.microsoft.unlock_account(user='cn={},cn={},dc={},dc={}'.format(username, config.container, config.dc1, config.dc2))
ad_c.extend.microsoft.modify_password(user='cn={},cn={},dc={},dc={}'.format(username, config.container, config.dc1, config.dc2), new_password=userpassword, old_password=None)
changeUACattribute = {"userAccountControl": (MODIFY_REPLACE, [512])}
ad_c.modify('cn={},cn={},dc={},dc={}'.format(username, config.container, config.dc1, config.dc2), changes=changeUACattribute)
I'm super new in development in general. I'm currently building a webapp that get data from Rally/CA Agile Central and put them in a neat table.
My code:
response = rally.get('UserStory', fetch = True, query=query_criteria)
response_defect = rally.get('Defect', fetch = True, query=query_criteria)
story_list = []
if not response.errors:
for story in response:
#print (story.details())
a_story={}
#a_story['State'] = story.State.Name #if story.State else "Backlog"
a_story['State']=story.BusOpsKanban if story.BusOpsKanban else "unassigned"
#a_story['Status']=Story.Status if story.Status else "unassigned"
a_story['id'] = story.FormattedID
a_story['name'] = story.Name
a_story['Opened']=(datetime.strptime(story.CreationDate, '%Y-%m-%dT%H:%M:%S.%fZ').strftime('%Y-%d-%b'))
a_story['Requester']= story.Owner.Name if story.Owner else "unassigned"
a_story['Blocked']= story.Blocked
a_story['Service']=story.c_ServiceNowID
My issue is to get access to the value of the linkid of my customfield (c_ServiceNowID).
When I run a Dict = I see that I have LinkID attributes but when I type
story.c_ServiceNowID.LinkID, I receive an error message telling me there is no such attributes.... How do I access this value using python ?
Thank you
According to the documentation at http://pyral.readthedocs.io/en/latest/overview.html#custom-fields, pyral allows you to reference the field without the c_ prefix
Most Artifact types in Rally can be augmented with custom fields. As of Rally WSAPI v2.0, the ElementName for a custom field is prefixed with ‘c_’. The pyral toolkit allows you to reference these fields without having to use the ‘c_’ prefix. For example, if your custom field has a DisplayName of ‘Burnt Offerings Index’ you can use the String of ‘BurntOfferingsIndex’ in a fetch clause or a query clause or refer to the field directly on an artifact as artifact.BurntOfferingsIndex.
I think what you have should work, unless the ServiceNowID is empty. In that case there will not be a LinkID or DisplayString available on the ServiceNowID object.
If you update your code to check to make sure the Attribute is there, does it work?
if hasattr(story.c_ServiceNowID, 'LinkID'):
a_story['Service']=story.c_ServiceNowID.DisplayString
a_story['Link']=story.c_ServiceNowID.LinkID
I need your help to order listed item.
I am trying to make apps that can send message to his/her friends ( just like social feeds ). After watching Bret Slatkin talk about create microblogging here's my code:
class Message(ndb.Model):
content = ndb.TextProperty()
created = ndb.DateTimeProperty(auto_now=True)
class MessageIndex(ndb.Model):
receivers = ndb.StringProperty(repeated=True)
class BlogPage(Handler):
def get(self):
if self.request.cookies.get("name"):
user_loggedin = self.request.cookies.get("name")
else:
user_loggedin = None
receive = MessageIndex.query(MessageIndex.receivers == user_loggedin)
receive = receive.fetch()
message_key = [int(r.key.parent().id()) for r in receive]
messages = [Message.get_by_id(int(m)) for m in message_key]
for message in messages:
self.write(message)
The first I do a query to get all message that has my name in the receivers. MessageIndex is child of Message, then I can get key of all message that I receive. And the last is I iter get_by_id using list of message key that I get.
This works fine, but I want to filter each message by its created datetime and thats the problem. The final output is listed item, which cant be ordered using .order or .filter
Maybe some of you can light me up.
You can use the message keys in an 'IN' clause in the Message query. Note that you will need to use the parent() key value, not the id() in this case.
eg:
# dtStart, dtEnd are datetime values
message_keys = [r.key.parent() for r in receive]
query = Message.query(Message._key.IN(message_keys), Message.created>dtStart, Message.created<dtEnd)
query = query.order(Message.created) # or -Message.created for desc
messages = query.fetch()
I am unsure if you wish to simply order by the Message created date, or whether you wish to filter using the date. Both options are catered for above.
I'm trying to add entries with python ldap. I'm getting a naming convention error. My code is
import ldap
import ldap.modlist as modlist
LOGIN = ""
PASSWORD = ''
LDAP_URL = "ldap://127.0.0.1:389"
user='grant'
l = ldap.initialize(LDAP_URL)
l.bind(LOGIN, PASSWORD)
dn="ou=Enki Users,dc=enki,dc=local"
attrs = {}
attrs['objectclass'] = ['top','organizationalRole','simpleSecurityObject']
attrs['cn'] = 'test'
attrs['userPassword'] = 'test'
attrs['description'] = 'User object for replication using slurpd'
# Convert our dict to nice syntax for the add-function using modlist-module
ldif = modlist.addModlist(attrs)
# Do the actual synchronous add-operation to the ldapserver
l.add_s(dn,ldif)
# Its nice to the server to disconnect and free resources when done
l.unbind_s()
The error is:
ldap.NAMING_VIOLATION: {'info': "00002099: NameErr: DSID-0305109C, problem 2005 (NAMING_VIOLATION), data 0, best match of:\n\t'dc=enki,dc=local'\n", 'desc': 'Naming violation'}
The code that runs but doesn't insert the user into the correc organizational unit is the following code. However even though it runs I can't find the user in active directory. Please help me find whats wrong. I'm basically making a django webform for user management.
import ldap
import ldap.modlist as modlist
LOGIN = ""
PASSWORD = ''
LDAP_URL = "ldap://127.0.0.1:389"
user='grant'
l = ldap.initialize(LDAP_URL)
l.bind(LOGIN, PASSWORD)
dn="cn=test,ou=Enki Users,dc=enki,dc=local"
attrs = {}
attrs['objectclass'] = ['top','organizationalRole','simpleSecurityObject']
attrs['cn'] = 'test'
attrs['userPassword'] = 'test'
attrs['description'] = 'User object for replication using slurpd'
# Convert our dict to nice syntax for the add-function using modlist-module
ldif = modlist.addModlist(attrs)
# Do the actual synchronous add-operation to the ldapserver
l.add_s(dn,ldif)
# Its nice to the server to disconnect and free resources when done
l.unbind_s()
I speculate (but have not tested to prove it) that the root cause of your error is that your entry does not contain a "naming attribute" that matches the leftmost attribute in the DN of your entry, which in your case is ou=Enki Users. To add this naming attribute to the entry, you can add the following line in the part of your code that populates the attrs dict.
attrs['ou'] = 'Enki Users'