I am using the ldap3 library (https://ldap3.readthedocs.io/en/latest/) with Python and authenticating against LDAP
conn = Connection(server, user='CN=person,OU=Service Accounts,DC=mydc,DC=mydomain,DC=co,DC=uk', password='Password123', auto_bind=True)
The below works but only because I know the person value. How would I set this up so someone can authenticate using their mail or user ID e.g. forename.surname
At the moment they would need to use the dn form which of course no user will ever be likely to know
Thanks
Using this page https://ldap3.readthedocs.io/en/latest/tutorial_intro.html#logging-into-the-server
I got the following to work
from ldap3 import Server, Connection, ALL, NTLM
server = Server('ldap://my_ldap_server', get_info='ALL')
conn = Connection(server, user="mydomain\\user", password='Password123', authentication=NTLM)
conn.bind()
authenticated = conn.bound
print(authenticated)
conn.unbind()
At the moment they would need to use the dn form which of course no user will ever be likely to know
With standard LDAP directories, you're supposed to bind with the application's own account first, then perform a search for some attribute as the username (e.g. search Active Directory for sAMAccountName=theuser), and finally use the found entry's DN as the actual bind DN for password verification.
For Active Directory in particular, you can directly specify either the UPN theuser#ad.example.com or the legacy SAM account name EXAMPLE\theuser in place of the bind DN.
Related
I'm using Firebase authentication to manage my users accounts.
Now, I need to change the uid of the users, then I'm trying to delete the user and import it again with the same password using python.
I'm trying to follow the documentation. But I might be missing something.
So, in the Firebase authentication page, I'm going to menu (in the right upper corner) and getting the base64_signer_key and base64_salt_separator values.
And trying to use the code below to delete the user, import the user and update the other fields:
for user in auth.list_users().iterate_all():
if user.email == 'myname#yahoo.com':
newId = CPF(cpf()).rawValue
oldId = user.uid
print('User: {}'.format(user._data))
# Delete the user
auth.delete_user(oldId)
# Recreate the user
users = [
auth.ImportUserRecord(
uid=newId,
email=user.email,
password_hash=user.password_hash.encode('utf-8'),
password_salt=None
),
]
hash_alg = auth.UserImportHash.scrypt(
key=base64.b64decode(base64_signer_key),
salt_separator=base64.b64decode(base64_salt_separator),
rounds=8,
memory_cost=14
)
try:
result = auth.import_users(users, hash_alg=hash_alg)
print('Successfully imported {0} users. Failed to import {1} users.'.format(
result.success_count, result.failure_count))
for err in result.errors:
print('Failed to import {0} due to {1}'.format(users[err.index].uid, err.reason))
except Exception as e:
print(e)
# Update user
auth.update_user(
newId,
phone_number=user.phone_number,
email_verified=user.email_verified,
display_name=user.display_name,
disabled=user.disabled
)
I'm following this documentation https://firebase.google.com/docs/auth/admin/import-users#import_users_with_firebase_scrypt_hashed_passwords
I'm able to delete and recreate the user, but when I try to login with the same user/password I'm getting FirebaseInvalidPasswordError.
What should I do recreate the user with same password and be able to authenticate in the standard way ?
After many tests, maybe I've managed to find a working way to solve the problem.
First of all, if you have created a new service account private key, go to GCP console here https://console.cloud.google.com/iam-admin/iam?authuser=0&project=[your_firebase-proect-id] and make sure your service account have the "Firebase Authentication" admin rights
(note the service account)
(check permission)
This was my first problem since without that permission, the firebase admin SDK always returns an empty password_salt and the string "UkVEQUNURUQ=" for the password_hash (which translates to "REDACTED").
Once I got the correct password hash and salt for user, your code should looks like this
# Recreate the user
users = [
auth.ImportUserRecord(
uid=newId,
email=user.email,
password_hash=base64.urlsafe_b64decode(user.password_hash),
password_salt=base64.urlsafe_b64decode(user.password_salt)
),
]
Note the base64.urlsafe_b64decode part? I've tried to manually export my probject users with the firebase cli though
firebase auth:export --project [project-id] users.csv
and noticed a big difference: Python password hash was
utfZLdz4phgAnRIKRUOxxFTKmbUEenbV1CbkQC0o4iorXpx-BJsdwofjAQkb1mUAgs_sO49cBv_lT8QuCztRzA== while CSV password hash was utfZLdz4phgAnRIKRUOxxFTKmbUEenbV1CbkQC0o4iorXpx+BJsdwofjAQkb1mUAgs/sO49cBv/lT8QuCztRzA== (in python slashes are undercores)
Don't know if my approach would cover all cases, but exporting auth from the cli and comparing their hashes with the python ones could lead you to solve further cases.
I have an ldap server who I connect to using `con = ldap.initialize(server)` and I bind to the server using `con.simple_bind_s(bind_dn, bind_password)`.
Now I want a authenticate a user using this ldap connection using his username and password which is different from the bind username and password. I searched a lot but didn't get any concrete answer.
Any help is appreciated.Thanks.
con.simple_bind_s(POST[bind_dn], POST[bind_password])
Can be used for both admin and users, something like this:
con.simple_bind_s(uid=1000,ou=people,o=org, password)
con.simple_bind_s(cn=Directory Manager, password)
In general, you can find ready to use modules in python that allow the management for users session using the same priciple, ceck this: https://turbogears.readthedocs.io/en/latest/cookbook/ldap-auth.html
So, I have two servers:
1) Dev
2) Prod
I am using pymssql.connect() for connecting to a SQL database to which Dev\user has access. The problem is that Prod\user does not have access, but it is the same password to login to both servers, thus if I could do something like, on the "prod" server
user = input("user:")
pymssql.connect("select 2",server_db,user=user,password=None)
then I can hard type user=dev\user and still use the password from authentication, which does not work. If I set user=None, then automatically user and pw is from the authentication, but is it not possible to extract either of those?
I cannot type the password since it is a job which runs each night.
I'm new to Flask and I'm trying out Flask-LDAP3-Login.
I've followed the documentation here and i have it working which is great: https://flask-ldap3-login.readthedocs.io/en/latest/index.html
How would i go about authenticating a user based on whether they are a member of a specific group? I see the docs mention group filtering but i'm not sure how to put it all together.
If anyone is curious, i solved this myself doing the following:
First, i integrated flask-ldap3-login with Flask-SQLAlchemy using steps here - https://github.com/nickw444/flask-ldap3-login/issues/26
My save user method now looks like this:
#ldap_manager.save_user
def save_user(dn, username, data, memberships):
id=int(data.get("uidNumber"))
if 'group-goes-here' in data.get("memberOf"):
user=User.query.filter_by(id=id).first()
if not user:
user=User(
id=int(id),
dn=dn,
username=username,
email=data['mail'],
firstname=data['givenName'],
lastname=data['sn']
)
db.session.add(user)
db.session.commit()
return user
So basically provided the user enters valid LDAP credentials it goes to AD to retrieve their group memberships and its a simple if 'group-goes-here' in data.get("memberOf"): determines whether to save the user in my User model and return it back to the handler.
#auth.route('/login', methods=['GET', 'POST'])
def login():
# Redirect users who are not logged in.
form = LDAPLoginForm()
if form.validate_on_submit():
if form.user:
login_user(form.user)
else:
flash('Login Failed', 'warning')
return redirect(url_for('auth.login'))
return redirect(url_for('main.home'))
Hope this helps!
The LDAP_USER_OBJECT_FILTER can be used to check group membership. If a user is not found within the group, an authentication fail will be produced. This is taken from the flask-ldap3-login docs:
Specifies what object filter to apply when searching for users. Defaults to '(objectclass=person)'
Matching direct members of a group:
app.config['LDAP_USER_OBJECT_FILTER'] = '(objectclass=person)(memberOf=<DN of Group>)'
For nested group membership you can use LDAP_MATCHING_RULE_IN_CHAIN:
app.config['LDAP_USER_OBJECT_FILTER'] = '(objectclass=person)(memberOf:1.2.840.113556.1.4.1941:=<DN of Group>)'
The Microsoft Search Filter Syntax docs state:
This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.
Do you have a ldap server ? if not go to https://www.openldap.org/ and follow instructions on how to install openldap server
if you prefer a docker container then go here
https://github.com/osixia/docker-openldap and follow steps to get the container up and running
then go here https://ldap3.readthedocs.io/tutorial.html
pip install ldap3 on the machine which has your python env or another python container (same bridge network as your ldap container)
open python console and type the following commands
>>> from ldap3 import Server, Connection, ALL
>>> server = Server('ipa.demo1.freeipa.org')
>>> conn = Connection(server)
>>> conn.bind()
True
first with this free ldap server ipa.demo1.freeipa.org
and then with your own ldap server ip address
I am working on an email client powered by Django and IMAPClient library.
On the client-side, users have interface for associating their mailboxes (like test1#gmail.com, test2#gmail.com, etc...) with their accounts in my app . And the goal is to store credentials info so that the user has to go through the procedure of inserting password and host name (like imap.gmail.com) only once unless he deliverately logs out from an account.
The question is, where and how should the login/password/hostname data used for establishing IMAP connection be stored? Is it my server's DB? Or some sort of .ini files? I remember, reading a post about python/imap where they stored it in .ini files, but I can't find it now.
Currently, I store credentials and IMAP hostname info in my local db. Thus, in each view that needs to fetch info from IMAP server, I query my server db to get the data and After this, I initialize IMAPClient variable for establishing connection with the IMAP server. And I have a terrible feeling that this is junk code:
def show_mail(request, login):
user_email_info = UserEmailInfo.objects.get(user_id = request.user.id,
login = login)
HOST = user_email_info.host #'imap.gmail.com'
USERNAME = user_email_info.login
PASSWORD = user_email_info.psw
server = IMAPClient(HOST, use_uid=True, ssl=True)
server.login(USERNAME, PASSWORD)
P.S. I am not sure whether this question is related more to general python/Django rather than IMAP. So if I am missing something, please give a hint on this, I'll edit the question in a timely manner.