Trying to Search 2 Folders for Users in LDAP - python

#Users Accounts path
bind_dn = "cn="+user+",ou=" +user[0]+",OU=Useraccounts,OU=Mtp,DC=us,DC=bosch,DC=com" and "cn="+user+",ou=" +user[0]+",OU=Useraccounts,OU=Ca1,DC=br,DC=bosch,DC=com"
bind_pass = password
If i do "and" it will only search the second folder
#Users Accounts path
bind_dn = "cn="+user+",ou=" +user[0]+",OU=Useraccounts,OU=Mtp,DC=us,DC=bosch,DC=com" or "cn="+user+",ou=" +user[0]+",OU=Useraccounts,OU=Ca1,DC=br,DC=bosch,DC=com"
bind_pass = password
When I do "or" it only searches the first folder
This is for LDAP authentication it is going in these folders to search for usernames to make sure they exist. Is there a function i can use to have this bind_dn search both folders for users instead of just one or the other.
Function to connect LDAP
def connect_ldap(user, password):
#CHANGE TO YOUR LDAP SERVER HERE
#LDAP Server
ldap_server = "bosch.com"
#CHANGE TO YOUR BIND_DN PATH HERE
#Users Accounts path
bind_dn = "cn="+user+",ou=" +user[0]+",OU=Useraccounts,OU=Mtp,DC=us,DC=bosch,DC=com" and "cn="+user+",ou=" +user[0]+",OU=Useraccounts,OU=Ca1,DC=br,DC=bosch,DC=com"
bind_pass = password
#Config the server and connection
server = Server(ldap_server, port=int(636), use_ssl=bool(True))
conn = Connection(server=server, user=bind_dn, password=bind_pass)
#First make a touchbase in the LDAP Server with the credentials to authenticate
connection_status = conn.bind()
print("Status: ",connection_status)
# If the user and pass is correct it will continue the script
if connection_status == True:
#Filter the search to Groups
search_filter = '(objectClass=group)'
try:
#CHANGE TO YOUR GROUP SEARCH HERE
#This search will return a members list of the selected group
conn.search("CN=CI/OSR-NA Staff,OU=Recipients,OU=MAIL34,OU=DL,OU=MSX,DC=us,DC=bosch,DC=com",
search_filter, search_scope=SUBTREE, attributes=['member'])
members = []
#Set the list in a variable
for entry in conn.entries:
members = entry.member.values
print("\nGroup Members: \n\n", members, "\n")
status = "Permission Denied"
#Check if the user is part of the group
for member in members:
#If the user is part of the group it will return "Permission Allowed" and terminate the script.
if user.lower() in member.lower() or user.upper() in member.upper():
status = "Permission Allowed"
return status
#If the user is not part of the group it will return "Permission Denied" and terminate the script.
if status == "Permission Denied":
return status
except Exception as e:
return e
# If the user and pass is incorrect it will return "False" and terminate the script.
elif connection_status == False:
return "Connection error"

bind_dn is a string, so it's converting whatever you have on the right to a single string. The reason for the difference in using and or or is because of how those keywords operate on strings.
a and b
means: If string a has a value, then use string b. In your case, because the first string is a constant string, it always has a value, so the result is always the second string.
a or b
means: If string a has a value, use that. Otherwise, use string b. In your case, because the first string is a constant string, it always has a value so that first string is always used.
This syntax is pointless with static strings, since the result will always be the same. You would usually use this syntax with variables. You can read this answer to read about cases where this is quite useful.
You will need to set that to a single string without and or or. You haven't shown the code where you use these variables, and I can't figure it out from the python-ldap documentation. But if you're using base_dn to authenticate, then what you do will depend on what your LDAP server is running. If you're using Active Directory, I know it will accept just the username - you don't need the full DN. So if you are using AD, you can just do this:
bind_dn = user

Related

Tried using Python flask to make data sent from HTML <form> a tuple to match selected data from a MySQL table if exit, but something went wrong

// SELECT
myDatabaseCursor.execute("SELECT username, password FROM member")
myDatabase.commit()
// get data from form to make a tuple
userCheck = (request.form["signInUsername"], request.form["signInPassword"])
// iterate selected data tuple into a list
results = []
for selectedData in myDatabaseCursor:
results.append(selectedData)
// check if there is a match in MySQL database
if userCheck in results:
session["status"]="logged"
session["user_name"]=request.form["signInUsername"]
return redirect("/member")
else:
return redirect("/error/?message=wrong username or password")
When I ran my server and tried typing in the username and the right password, successfully logged in; tried typing in the username and the wrong password, which, didn't have any match in the database, got rejected logging in. ALL GOOD...
BUT, when I tried typing in the username and the wrong password, which, HAS A MATCH IN THE PASSWORD COLUMN THOUGH DOESN'T BELONG TO THE RIGHT USERNAME, still successfully logged in.
I am really confused now, hope you guys have any idea about this situation.
Thanks, appreciate your replies.
You could change your query to support WHERE clause. Something along the lines of:
# get data from form to make a tuple
username, password = (
request.form["signInUsername"],
request.form["signInPassword"]
)
# SELECT
myDatabaseCursor.execute(
"""
SELECT username, password
FROM member
WHERE username = '{username}' AND password = '{password}'
""".format(username=username, password=password)
)
myDatabase.commit()
# set userCheck to True or False if the myDatabaseCursor result is not empty..
# TODO
# if row was in returned table
if userCheck:
session["status"]="logged"
session["user_name"]=request.form["signInUsername"]
return redirect("/member")
else:
return redirect("/error/?message=wrong username or password")
Probably the problem lies in the session['status']. You never set it to e.g. "unlogged", so if you don't close the browser, the status will always be 'logged' after first successful login.
Try to initialize your variable at the beginning of the script, i.e. session["status"]=None and then in every other page check that the status is actually 'Logged' as you're probably already doing.
session["status"]=None
// SELECT
myDatabaseCursor.execute("SELECT username, password FROM member")
myDatabase.commit()
// get data from form to make a tuple
userCheck = (request.form["signInUsername"], request.form["signInPassword"])
// iterate selected data tuple into a list
results = []
for selectedData in myDatabaseCursor:
results.append(selectedData)
// check if there is a match in MySQL database
if userCheck in results:
session["status"]="logged"
session["user_name"]=request.form["signInUsername"]
return redirect("/member")
else:
return redirect("/error/?message=wrong username or password")
In any case, for the sake of best practice, you should amend your code to apply the logic depicted by #matthewking, retrieving just the password you need to check.

Is there a way to get a username from a user_id from stored user_id's even if they aren't on the same server?

I was wondering if it's possible to get a username from a user id globally on discord.
within a Cog, I am currently using the line below to retrieve information if the search is local.
self.ctx.guild.get_member
But I'm hoping to expand into a global search, but I'm not even sure if it's possible
I was also thinking if I can't get the username, I'd want to change the NULL return I get into something like "External User"
I'm using this line to make the search and iterate through a list (Local version)
table = ("\n".join(f'{idx + 1}. {self.ctx.guild.get_member(entry[0])} (XP: {entry[1]} | Level: {entry[2]} \n' for idx, entry in enumerate(entries)))
You can use user = self.get_user(USERID) to get any user on discord, and user.name to get their username. Look at https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.get_user to find more about get_user.
The other answer only works if you have the user cached. This is not guaranteed, and in larger bots is almost guaranteed to not be unless you have the members intent enabled.
You can use bot.fetch_user, which will give you the user object regardless of your cache. It's still a good idea to check .get_user first.
I often have a function like this in my code:
def get_or_fetch_user(bot, user_id):
user = bot.get_user(user_id)
if not user: # the user isn't in the cache, or it doesn't exist
try:
user = await bot.fetch_user(user_id)
except discord.NotFound: # fetch_user raises an error if the user doesn't exist
user = None
return user

Retrieve a value from MongoDB and set a string to it in Python(Pymongo)

I am trying to retrieve a value from MongoDB and set a string to it in Python with MongoDB. Here is my user registration function:
def registerAccount():
registerUsername = input('Please input a username to be registered into our database.')
registerPassword = input('Please input a password to be entered into our database.')
passwordHash = sha256_crypt.hash(registerPassword)
regDetails = {
"username": registerUsername,
"passwordhash": passwordHash
}
db = client.pinnacle
users = db.users
users.insert(regDetails)
and here is my login function:
def login():
db = client.pinnacle
users = db.users
pwHash = users.find_one({"passwordhash"})
print(str(pwHash))
loginUsername = input('Please input username to be signed in.')
loginPassword = input('Please input password to be signed in.')
# pprint.pprint(users.find_one({"username": "1"}))
# example = users.find_one({"username": "1"})
pbkdf2_sha256.verify(loginPassword, pwHash)
Basically, I need to search the database for the username, and get the string right after the username which is passwordHash. Then I will set pwHash to that string which will be checked with PassLib. Help is appreciated. Thanks!
Also if anyone is interested you can see my full code here.
this happens whenever i select to login, even after not typing in username or password
The error is caused by this line below:
pwHash = users.find_one({"passwordhash"})
Where you should specify a filter of key/value pair, instead it's only specifying a key. You should remove the line above (and the print line right after it). See also pymongo find_one() method for more info.
Also, in the same function login(), you have a typo for key username.
user = users.find_one({"userName": loginUsername})
Note that it should be lowercase username to be consistent with how you're storing them elsewhere in the code.
Lastly, you should replace
pbkdf2_sha256.verify(loginPassword, user['passwordhash'])
with below method, as this is how you encrypt it in the registerAccount()
sha256_crypt.verify(loginPassword, user['passwordhash'])
Please consider adding error checks throughout your code. i.e. if users.find_one() returns None do XYZ.

Changing Active Directory user password in Python 3.x

I am trying to make a Python script that will open an LDAP connection to a server running AD, take a search entry (in this case a name), search for that entry and change that users password to a randomly generated password (as well as set the option to change password on logon) and then send them an automated secure email containing the new temporary password.
So far I have been able to connect to the server, and search for a single DN which returns. The temporary password is being generated, and an email is being sent (although the password is not hashed, and the email is not secure yet). However, I cannot find any information on where to go from here.
I have found Change windows user password with python however I see that this does not play well with AD, and the other LDAP in Python documentation I have been finding seems to be outdated from 2.x and no longer works. The documentation for ldap3 (https://media.readthedocs.org/pdf/ldap3/stable/ldap3.pdf) also doesnt seem to really mention anything for it, and exhaustive Googling has been fruitless. I am new to this kind of programming having only low level or academic knowledge previously, so this has been a bit frustrating but Python is my strongest language.
----------------EDITED CODE TO CURRENT STATUS-----------------------
#Takes input for name which will be used for search criterion
zid = input("ZID: ")
zid = str(zid).lower()
print(zid)
#Binds session to the server and opens a connection
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com' %zid, password = "<something>", auto_bind=True)
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com' %zid, password = "<something>", auto_bind=True)
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com', password = "<something>", auto_bind=True) %zid
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
sys.exit(0)
#Searches and prints LDAP entries
try:
base_dn = 'DC=<something>,DC=<something>,DC=<something>,DC=<something>,DC=com'
zid_filter = '(sAMAccountName=%s)' %zid
conn.search(base_dn, zid_filter, attributes=['mail'])
#i.e. "DN: CN=<First Last>,OU=<something>, DC= <something>
user_dn = str(conn.entries)
#i.e. "CN=<First Last>"
front = user_dn.find('C')
back = user_dn.find(',')
user_cn = user_dn[front:back]
#i.e. "<First Last>"
display_name = user_cn[3:]
#i.e. "first.last#<something>.com"
raw_email = str(conn.entries)
front = raw_email.find('mail: ')
back = raw_email.find('#<something>.com')
user_email = raw_email[front + 6:back] + '#<something>.com'
except:
print("Could not search entries")
#Generates random 12 digit alpha-numeric password
try:
new_password = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(12))
print(new_password)
print("New password successfully generated")
except:
print("New password could not be generated")
#Set and replace AD Password
try:
conn.extend.microsoft.modify_password(user_dn, None, new_password)
print ("Active Directory password was set successfully!")
except:
print('Error setting AD password')
sys.exit(0)
Any suggestions on how to get/set the user password and hash the password for security purposes during this whole ordeal? For the email I imagine I can force it to use HTTPS and that would be sufficient, but the connection to the server passing the new_password to I would like to secure.
ldap3 contains a specific method for changing AD password, just add the following after you generated a new password:
dn = conn.entries[0].entry_get_dn() # supposing you got back a single entry
conn.extend.microsoft.modify_password(dn, None, new_password)
This should properly encode the password and store it in AD.
This code is working with Windows 2012 R2 AD:
First, install latest ldap3:
sudo pip3 install ldap
#!/usr/bin/python3
import ldap3
SERVER='127.0.0.1'
BASEDN="DC=domain,DC=com"
USER="user_domain_login_name#domain.com"
CURREENTPWD="current_password"
NEWPWD="new_password"
SEARCHFILTER='(&(userPrincipalName='+USER+')(objectClass=person))'
USER_DN=""
USER_CN=""
ldap_server = ldap3.Server(SERVER, get_info=ldap3.ALL)
conn = ldap3.Connection(ldap_server, USER, CURREENTPWD, auto_bind=True)
conn.start_tls()
print(conn)
conn.search(search_base = BASEDN,
search_filter = SEARCHFILTER,
search_scope = ldap3.SUBTREE,
attributes = ['cn', 'givenName', 'userPrincipalName'],
paged_size = 5)
for entry in conn.response:
if entry.get("dn") and entry.get("attributes"):
if entry.get("attributes").get("userPrincipalName"):
if entry.get("attributes").get("userPrincipalName") == USER:
USER_DN=entry.get("dn")
USER_CN=entry.get("attributes").get("cn")
print("Found user:", USER_CN)
if USER_DN:
print(USER_DN)
print(ldap3.extend.microsoft.modifyPassword.ad_modify_password(conn, USER_DN, NEWPWD, CURREENTPWD, controls=None))
else:
print("User DN is missing!")

LDAP query in python

I want to execute the following query in the ldap
ldapsearch -h hostname -b dc=ernet,dc=in -x "(&(uid=w2lame)(objectClass=posixAccount))" gidnumber
ldapsearch -h hostname -b dc=ernet,dc=in -x "(&(gidNumber=1234)(objectClass=posixGroup))" cn
And use the variables thus obtained. How can I do that?
While the accepted answer does in fact show a proper way to bind to an LDAP server I do feel it didn't answer the question holistically. Here is what I ended up implementing to grab the mail and department of a user. This somewhat blends the required attributes from the original question.
l = ldap.initialize('ldap://ldap.myserver.com:389')
binddn = "cn=myUserName,ou=GenericID,dc=my,dc=company,dc=com"
pw = "myPassword"
basedn = "ou=UserUnits,dc=my,dc=company,dc=com"
searchFilter = "(&(gidNumber=123456)(objectClass=posixAccount))"
searchAttribute = ["mail","department"]
#this will scope the entire subtree under UserUnits
searchScope = ldap.SCOPE_SUBTREE
#Bind to the server
try:
l.protocol_version = ldap.VERSION3
l.simple_bind_s(binddn, pw)
except ldap.INVALID_CREDENTIALS:
print "Your username or password is incorrect."
sys.exit(0)
except ldap.LDAPError, e:
if type(e.message) == dict and e.message.has_key('desc'):
print e.message['desc']
else:
print e
sys.exit(0)
try:
ldap_result_id = l.search(basedn, searchScope, searchFilter, searchAttribute)
result_set = []
while 1:
result_type, result_data = l.result(ldap_result_id, 0)
if (result_data == []):
break
else:
## if you are expecting multiple results you can append them
## otherwise you can just wait until the initial result and break out
if result_type == ldap.RES_SEARCH_ENTRY:
result_set.append(result_data)
print result_set
except ldap.LDAPError, e:
print e
l.unbind_s()
You probably want to use the ldap module. Code would look something like:
import ldap
l = ldap.initialize('ldap://ldapserver')
username = "uid=%s,ou=People,dc=mydotcom,dc=com" % username
password = "my password"
try:
l.protocol_version = ldap.VERSION3
l.simple_bind_s(username, password)
valid = True
except Exception, error:
print error
Here's an example generator for python-ldap.
The ldap_server is the object you get from ldap.initialize(). You will probably need to bind before calling this function, too, depending on what LDAP server you are using and what you are trying to query for. The base_dn and filter_ are similar to what you've got in your command line version. The limit is the maximum number of records returned.
def _ldap_list(ldap_server, base_dn, filter_, limit=0):
""" Generator: get a list of search results from LDAP asynchronously. """
ldap_attributes = ["*"] # List of attributes that you want to fetch.
result_id = ldap_server.search(base_dn, ldap.SCOPE_SUBTREE, filter_, ldap_attributes)
records = 0
while 1:
records += 1
if limit != 0 and records > limit:
break
try:
result_type, result_data = ldap_server.result(result_id, 0)
except ldap.NO_SUCH_OBJECT:
raise DirectoryError("Distinguished name (%s) does not exist." % base_dn)
if result_type == ldap.RES_SEARCH_ENTRY:
dn = result_data[0][0]
data = result_data[0][1]
yield dn, data
else:
break
Please keep in mind that interpolating user-provided values into your LDAP query is dangerous! It's a form of injection that allows a malicious user to change the meaning of the query. See: http://www.python-ldap.org/doc/html/ldap-filter.html
I cobbled this together this morning while skimming through the documentation of the ldap module. It can fulfil the requirements of the OP changing the filter and the other settings to his liking.
The documentation of the ldap module is pretty good if you understand the context (that's what took me a while). And the module is surprinsingly easy to use. We have a similar script written in bash using ldapserach that is at least 3 or 4 times longer and more complex to read.
This code accepts a partial search string (email, name, uid or part of it) and returns the results in LDIF format. The idea is to make it very simple to use for a very specific task and if possible without using flags so that my less skilled co-workers can find the relevant info quickly.
Note that this is written for an LDAP server that runs on a machine that is not accessible from outside our internal network and which is secured with 2FA authentication. It can, thus, safely accept anonymous queries. But adding user and password should be trivial.
#! /usr/bin/python3
### usearch
### searches in the LDAP database for part of a name, uid or email and returns mail, uid, and full name
import ldap
import argparse
import sys
import ldif
l = ldap.initialize('ldaps://your.fancy.server.url', bytes_mode=False)
basedn = "dc=foo,dc=bar,dc=baz"
## ARGPARSE stuff!!!
parser=argparse.ArgumentParser(
description ='searches the LDAP server',
usage='usearch PARTIAL_MATCH (email, name, username)',
formatter_class = argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('istr', help='searches stuffz')
parser.print_help
args = parser.parse_args(None if sys.argv[1:] else ['-h'])
str1 = args.istr
sfilter = "(|(sn=*{}*)(mail=*{}*)(uid=*{}*))".format(str1,str1,str1)
attributes = ["mail","uid","cn"]
scope = ldap.SCOPE_SUBTREE
r = l.search_s(basedn,scope,sfilter,attributes)
ldif_writer=ldif.LDIFWriter(sys.stdout)
for dn, entry in r:
ldif_writer.unparse(dn,entry)
And as I was at it, here a version with the ldap3 module. The argparse part is copy-pasted. This time the output is "human readable", instead of LDIF:
#! /usr/bin/python3
## usearch3
## LDAP3 version
import ldap3
import argparse
import sys
server = ldap3.Server('ldaps://foo.bar.baz')
conn = ldap3.Connection(server)
conn.bind()
basedn = 'dc=foobar,dc=dorq,dc=baz'
attribs = ['mail','uid','cn']
parser=argparse.ArgumentParser(
description ='searches the LDAP server and returns user, full name and email. Accepts any partial entry',
usage='usearch3 PARTIAL_MATCH (email, name, username)',
formatter_class = argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('istr', help='searches stuffz')
parser.print_help
args = parser.parse_args(None if sys.argv[1:] else ['-h'])
str1 = args.istr
sfilter = "(|(sn=*{}*)(mail=*{}*)(uid=*{}*))".format(str1,str1,str1)
conn.search(basedn,sfilter)
conn.search(basedn,sfilter,attributes = attribs)
leng = len(conn.entries)
for i in range(leng):
user = conn.entries[i].uid
fullname = conn.entries[i].cn
email = conn.entries[i].mail
print("user:\t{}\nname:\t{}\nemail:\t{}\n\n".format(user,fullname,email))
you can use the commands module, and the getoutput to parse the result of the ldap query:
from commands import getoutput
result = getoutput('ldapsearch -h hostname -b dc=ernet,dc=in -x "(&(uid=w2lame)(objectClass=posixAccount))"')
print result
you have to have ldapsearch binary installed in your system.

Categories

Resources