{ "info": { "author": "Sean Whalen", "author_email": "whalenster@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Security" ], "description": "easyad\n======\n\nA simple Python module for common Active Directory authentication and lookup tasks\n\n::\n\n Copyright 2016 Sean Whalen\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\nWhy?\n----\n\nMost LDAP solutions for Python and/or Flask focus in being generic LDAP\ninterfaces. It's up to the developer to understand and work around the\nquirks of Active Directory. This module aims to reduce the complexity\nand development time for Python-powered applications that securely\ninterface with Active Directory.\n\nFeatures\n--------\n\n- Python 2 and 3 support\n- Unicode support\n- Authenticate user credentials via direct bind\n- Quickly test if a user is a member of a group, including nested groups\n- Query user and group attributes\n- Simple user and group search\n- Get all groups that a user is a member of, including nested groups\n- Get a list of all group member users, including from nested groups\n- Options to automatically convert binary data into base64 for JSON-safe\n output\n\n\nInstalling\n----------\n\nFirst, install the system dependencies\n\n::\n\n $ sudo apt-get install libsasl2-dev python3-dev python3-pip libldap2-dev libssl-dev\n\nThen\n\n::\n\n $ sudo pip3 install -U easyad\n\nExample uses\n------------\n\n::\n\n from __future__ import unicode_literals, print_function\n\n from getpass import getpass\n from json import dumps\n\n from easyad import EasyAD\n\n # Workaround to make input() return a string in Python 2 like it does in Python 3\n # It's 2016...you should really be using Python 3\n try:\n input = raw_input\n except NameError:\n pass\n\n # Set up configuration. You could also use a Flask app.config\n config = dict(AD_SERVER=\"ad.example.net\",\n AD_DOMAIN=\"example.net\",\n CA_CERT_FILE=\"myrootca.crt\")\n\n # Initialize all the things!\n ad = EasyAD(config)\n\n # Authenticate a user\n username = input(\"Username: \")\n password = getpass(\"Password: \")\n\n local_admin_group_name = \"LocalAdministrators\"\n\n user = ad.authenticate_user(username, password, json_safe=True)\n\n if user:\n # Successful login! Let's print your details as JSON\n print(dumps(user, sort_keys=True, indent=2, ensure_ascii=False))\n\n # Lets find out if you are a member of the \"LocalAdministrators\" group\n print(ad.user_is_member_of_group(user, local_admin_group_name))\n else:\n print(\"Those credentials are invalid. Please try again.\")\n exit(-1)\n\n # You can also add service account credentials to the config to do lookups without\n # passing in the credentials on every call\n ad.config[\"AD_BIND_USERNAME\"] = \"SA-ADLookup\"\n ad.config[\"AD_BIND_PASSWORD\"] = \"12345LuggageAmazing\"\n\n user = ad.get_user(\"maurice.moss\", json_safe=True)\n print(dumps(user, sort_keys=True, indent=2, ensure_ascii=False))\n\n group = ad.get_group(\"helpdesk\", json_safe=True)\n print(dumps(user, sort_keys=True, indent=2, ensure_ascii=False))\n\n print(\"Is Jen a manager?\")\n print(ad.user_is_member_of_group(\"jen.barber\", \"Managers\"))\n\n # The calls below can be taxing on an AD server, especially when used frequently.\n # If you just need to check if a user is a member of a group use\n # EasyAD.user_is_member_of_group(). It is *much* faster.\n\n # I wonder who all is in the \"LocalAdministrators\" group? Let's run a\n # query that will search in nested groups.\n print(dumps(ad.get_all_users_in_group(local_admin_group_name, json_safe=True)))\n\n # Let's see all of the groups that Moss in in, including nested groups\n print(dumps(ad.get_all_user_groups(user), indent=2, ensure_ascii=False))\n\neasyad methods\n--------------\n\nconvert_ad_timestamp(timestamp, json_safe=False)\n\n::\n\n Converts a LDAP timestamp to a datetime or a human-readable string\n\n Args:\n timestamp: the LDAP timestamp\n json_safe: If true, return a a human-readable string instead of a datetime\n\n Returns:\n A datetime or a human-readable string\n\n\nenhance_user(user, json_safe=False)\n\n::\n\n Adds computed attributes to AD user results\n\n Args:\n user: A dictionary of user attributes\n json_safe: If true, converts binary data into base64,\n And datetimes into human-readable strings\n\n Returns:\n An enhanced dictionary of user attributes\n\nprocess_ldap_results(results, json_safe=False)\n\n::\n\n Converts LDAP search results from bytes to a dictionary of UTF-8 where possible\n\n Args:\n results: LDAP search results\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n A list of processed LDAP result dictionaries.\n\neasyad.ADConnection\n-------------------\n\n::\n\n A LDAP configuration abstraction class\n\n Attributes:\n config: The configuration dictionary\n ad:The LDAP interface instance\n\n\nADConnection.__init__(self, config)\n\n::\n\n\n Initializes an ADConnection object\n\n Args:\n config: A dictionary of configuration settings\n Required:\n AD_SERVER: The hostname of the Active Directory Server\n Optional:\n AD_REQUIRE_TLS: Require a TLS connection. True by default.\n AD_CA_CERT_FILE: The path to the root CA certificate file\n AD_PAGE_SIZE: Overrides the default page size of 1000\n AD_OPTIONS: A dictionary of other python-ldap options\n\n\nADConnection.bind(self, credentials=None)\n\n::\n\n Attempts to bind to the Active Directory server\n\n Args:\n credentials: A optional dictionary of the username and password to use.\n If credentials are not passed, the credentials from the initial EasyAD configuration are used.\n\n Returns:\n True if the bind was successful\n\n Raises:\n ldap.LDAP_ERROR\n\nADConnection.unbind(self)\n\n::\n\n Unbind from the Active Directory server\n\neasyad.EasyAD\n-------------\n\n::\n\n A high-level class for interacting with Active Directory\n\n Attributes:\n user_attributes: A default list of attributes to return from a user query\n group_attributes: A default list of attributes to return from a user query\n\nEasyAD.__init__(self, config)\n\n::\n\n Initializes an EasyAD object\n\n Args:\n config: A dictionary of configuration settings\n Required:\n AD_SERVER: the hostname of the Active Directory Server\n AD_DOMAIN: The domain to bind to, in TLD format\n Optional:\n AD_REQUIRE_TLS: Require a TLS connection. True by default.\n AD_CA_CERT_FILE: the path to the root CA certificate file\n AD_BASE_DN: Overrides the base distinguished name. Derived from AD_DOMAIN by default.\n\n\nEasyAD.authenticate_user(self, username, password, base=None, attributes=None, json_safe=False)\n\n::\n\n Test if the given credentials are valid\n\n Args:\n username: The username\n password: The password\n base: Optionally overrides the base object DN\n attributes: A list of user attributes to return\n json_safe: Convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n A dictionary of user attributes if successful, or False if it failed\n\n Raises:\n ldap.LDAP_ERROR\n\nEasyAD.get_all_user_groups(self, user, base=None, credentials=None, json_safe=False)\n\n::\n\n Returns a list of all group DNs that a user is a member of, including nested groups\n\n Args:\n user: A username, distinguishedName, or a dictionary containing a distinguishedName\n base: Overrides the configured base object dn\n credentials: An optional dictionary of the username and password to use\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n A list of group DNs that the user is a member of, including nested groups\n\n Raises:\n ldap.LDAP_ERROR\n\n Notes:\n This call can be taxing on an AD server, especially when used frequently.\n If you just need to check if a user is a member of a group,\n use EasyAD.user_is_member_of_group(). It is *much* faster.\n\n\nEasyAD.get_all_users_in_group(self, group, base=None, credentials=None, json_safe=False)\n\n::\n\n Returns a list of all user DNs that are members of a given group, including from nested groups\n\n Args:\n group: A group name, cn, or dn\n base: Overrides the configured base object dn\n credentials: An optional dictionary of the username and password to use\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n A list of all user DNs that are members of a given group, including users from nested groups\n\n Raises:\n ldap.LDAP_ERROR\n\n Notes:\n This call can be taxing on an AD server, especially when used frequently.\n If you just need to check if a user is a member of a group,\n use EasyAD.user_is_member_of_group(). It is *much* faster.\n\n\nEasyAD.get_group(self, group_string, base=None, credentials=None, attributes=None, json_safe=False)\n\n::\n\n Searches for a unique group object and returns its attributes\n\n Args:\n group_string: A group name, cn, or dn\n base: Optionally override the base object dn\n credentials: A optional dictionary of the username and password to use.\n If credentials are not passed, the credentials from the initial EasyAD configuration are used.\n attributes: An optional list of attributes to return. Otherwise uses self.group_attributes.\n To return all attributes, pass an empty list.\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n A dictionary of group attributes\n\n Raises:\n ValueError: Query returned no or multiple results\n ldap.LDAP_ERROR: An LDAP error occurred\n\n\nEasyAD.get_user(self, user_string, json_safe=False, credentials=None, attributes=None)\n\n::\n\n Searches for a unique user object and returns its attributes\n\n Args:\n user_string: A userPrincipalName, sAMAccountName, or distinguishedName\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n credentials: A optional dictionary of the username and password to use.\n If credentials are not passed, the credentials from the initial EasyAD configuration are used.\n attributes: An optional list of attributes to return. Otherwise uses self.user_attributes.\n To return all attributes, pass an empty list.\n\n Returns:\n A dictionary of user attributes\n\n Raises:\n ValueError: query returned no or multiple results\n\n\nEasyAD.resolve_group_dn(self, group, base=None, credentials=None, json_safe=False)\n\n::\n\n Returns a group's DN when given a principalAccountName, sAMAccountName, email, or DN\n\n Args:\n group: A group name, CN, or DN, or a dictionary containing a DN\n base: Optionally overrides the base object DN\n credentials: An optional dictionary of the username and password to use\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n The groups's DN\n\n Raises:\n ldap.LDAP_ERROR\n\nEasyAD.resolve_user_dn(self, user, base=None, credentials=None, json_safe=False)\n\n::\n\n Returns a user's DN when given a principalAccountName, sAMAccountName, email, or DN\n\n Args:\n user: A principalAccountName, sAMAccountName, email, DN, or a dictionary containing a DN\n base: Optionally overrides the base object DN\n credentials: An optional dictionary of the username and password to use\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n The user's DN\n\n Raises:\n ldap.LDAP_ERROR\n\nsearch(self, base=None, scope=ldap.SCOPE_SUBTREE, filter_string=\"(objectClass=*)\", credentials=None,\n attributes=None, json_safe=False, page_size=None)\n\n::\n\n\n Run a search of the Active Directory server, and get the results\n\n Args:\n base: Optionally override the DN of the base object\n scope: Optional scope setting, subtree by default.\n filter_string: Optional custom filter string\n credentials: Optionally override the bind credentials\n attributes: A list of attributes to return. If none are specified, all attributes are returned\n json_safe: If true, convert binary data to base64, and datetimes to human-readable strings\n page_size: Optionally override the number of results to return per LDAP page\n\n Returns:\n Results as a list of dictionaries\n\n Raises:\n ldap.LDAP_ERROR\n\n Notes:\n Setting a small number of search_attributes and return_attributes reduces server load and bandwidth\n respectively\n\n\nsearch_for_groups(self, group_string, base=None, search_attributes=None, return_attributes=None,\n credentials=None, json_safe=False)\n\n::\n\n Returns matching group objects as a list of dictionaries\n\n Args:\n group_string: The substring to search for\n base: Optionally override the base object's DN\n search_attributes: The attributes to search through, with binary data removed\n easyad.EasyAD.group_attributes by default\n return_attributes: A list of attributes to return. easyad.EasyAD.group_attributes by default\n credentials: Optionally override the bind credentials\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n Results as a list of dictionaries\n\n Raises:\n ldap.LDAP_ERROR\n\n Notes:\n Setting a small number of search_attributes and return_attributes reduces server load and bandwidth\n respectively\n\nsearch_for_users(self, user_string, base=None, search_attributes=None, return_attributes=None, credentials=None,\n json_safe=False)\n\n::\n\n Returns matching user objects as a list of dictionaries\n\n Args:\n user_string: The substring to search for\n base: Optionally override the base object's DN\n search_attributes: The attributes to search through, with binary data removed\n easyad.EasyAD.user_attributes by default\n return_attributes: A list of attributes to return. easyad.EasyAD.user_attributes by default\n credentials: Optionally override the bind credentials\n json_safe: If true, convert binary data to base64 and datetimes to human-readable strings\n\n Returns:\n Results as a list of dictionaries\n\n Raises:\n ldap.LDAP_ERROR\n\n Notes:\n Setting a small number of search_attributes and return_attributes reduces server load and bandwidth\n respectively\n\n\nEasyAD.user_is_member_of_group(self, user, group, base=None, credentials=None)\n\n::\n\n Tests if a given user is a member of the given group\n\n Args:\n user: A principalAccountName, sAMAccountName, email, or DN\n group: A group name, cn, or dn\n base: An optional dictionary of the username and password to use\n credentials: An optional dictionary of the username and password to use\n\n Raises:\n ldap.LDAP_ERROR\n\n Returns:\n A boolean that indicates if the given user is a member of the given group\n\n\n\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/seanthegeek/easyad", "keywords": "ActiveDirectory,WindowsServer,authentication,LDAP", "license": "Apache 2.0", "maintainer": "", "maintainer_email": "", "name": "easyad", "package_url": "https://pypi.org/project/easyad/", "platform": "", "project_url": "https://pypi.org/project/easyad/", "project_urls": { "Homepage": "https://github.com/seanthegeek/easyad" }, "release_url": "https://pypi.org/project/easyad/1.0.9/", "requires_dist": [ "pyldap" ], "requires_python": "", "summary": "A simple Python module for common Active Directory authentication and lookup tasks", "version": "1.0.9" }, "last_serial": 2666405, "releases": { "1.0.1": [ { "comment_text": "", "digests": { "md5": "d19f93c6dbff34582b62c6f971d80c33", "sha256": "5a9701a4f9bd83b69f9e08e21b66f0e4ebab7128f17e67a36abe49dcfa2145f8" }, "downloads": -1, "filename": "easyad-1.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d19f93c6dbff34582b62c6f971d80c33", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16289, "upload_time": "2016-10-05T12:52:38", "url": "https://files.pythonhosted.org/packages/dc/8a/3bc567f424179357787dd4ad092beb46fe57ebea58525e3a8bb7f143cf60/easyad-1.0.1-py2.py3-none-any.whl" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "7c445aa6631c8ac9fca63741c6b15c3c", "sha256": "02667c53c6b3ccb25ddab05a93de2ffbb129e278b0fe25eb2f311a3de628ed22" }, "downloads": -1, "filename": "easyad-1.0.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "7c445aa6631c8ac9fca63741c6b15c3c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16303, "upload_time": "2016-10-06T18:23:10", "url": "https://files.pythonhosted.org/packages/ad/40/13f997dce63b44e1c24ec0fd2a50ad451440c1794929f25bc8e64e944ed0/easyad-1.0.2-py2.py3-none-any.whl" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "c3135fa0ea45f12b941ee0c00ec8f31c", "sha256": "5717ce85251936c69e2dcfb14967e4a97985e487bc7991fb08a67c140c9b2385" }, "downloads": -1, "filename": "easyad-1.0.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c3135fa0ea45f12b941ee0c00ec8f31c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16327, "upload_time": "2016-10-13T13:44:44", "url": "https://files.pythonhosted.org/packages/67/30/fbeb5b1ecc2a8389e73321949c6b31befce0a89f88d4dda5866f24cb09bf/easyad-1.0.3-py2.py3-none-any.whl" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "031d688bc7f77997308bc248af1c342a", "sha256": "48ae277bd3dd44d841ab3d5ce7c2d6bbad083afb2efa0c5d14c86906a23ae92a" }, "downloads": -1, "filename": "easyad-1.0.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "031d688bc7f77997308bc248af1c342a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16370, "upload_time": "2016-10-13T15:29:36", "url": "https://files.pythonhosted.org/packages/ce/96/ea4c4fc09adedcd1411d4427cff4b1fc0ca77a1d738da124e5c030ceb7a0/easyad-1.0.4-py2.py3-none-any.whl" } ], "1.0.5": [ { "comment_text": "", "digests": { "md5": "19efe0934d8470e964c2b5038911d36b", "sha256": "b7389ff563d69f87ac78fbb94b8c5ebdcf82312a9af8bdcc9c61fb457db7dcac" }, "downloads": -1, "filename": "easyad-1.0.5-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "19efe0934d8470e964c2b5038911d36b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16393, "upload_time": "2016-10-13T15:54:33", "url": "https://files.pythonhosted.org/packages/5e/f8/ce5ecd8634eed31d86911963fa07e8e14ffd3e7b3d2bf84d6764a29a1e1b/easyad-1.0.5-py2.py3-none-any.whl" } ], "1.0.6": [ { "comment_text": "", "digests": { "md5": "5b6e1d5bb3ef47adcb8b76bff55eb562", "sha256": "da07d8e20036a7d5ea1731e33246f4d1cc740de6824df1d68da6d88c11cc49b0" }, "downloads": -1, "filename": "easyad-1.0.6-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5b6e1d5bb3ef47adcb8b76bff55eb562", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16675, "upload_time": "2016-10-14T13:05:21", "url": "https://files.pythonhosted.org/packages/38/c6/5bea86cde9d2171956515280f70695fb9e7ccea201ec6cb596bd90102fa6/easyad-1.0.6-py2.py3-none-any.whl" } ], "1.0.7": [ { "comment_text": "", "digests": { "md5": "e73667eb2b5f51b0951ee97fc3eeda85", "sha256": "b07ab493c904ba8e7fb74bd411e0d800d962190d9a993584d80263f2a05d19c2" }, "downloads": -1, "filename": "easyad-1.0.7-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e73667eb2b5f51b0951ee97fc3eeda85", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16673, "upload_time": "2016-11-10T01:24:51", "url": "https://files.pythonhosted.org/packages/6b/66/505dc448592d1f870b40c21cec27a1d26b39e08d5cb79d51a1380fcbedc6/easyad-1.0.7-py2.py3-none-any.whl" } ], "1.0.8": [ { "comment_text": "", "digests": { "md5": "c8671ec17b42ecbce905f42f13af2de6", "sha256": "dbfe2a7375e01c93dd4c82bc2a8afd9c91b88cdfcbdd7bfbf546f9889ae92bb0" }, "downloads": -1, "filename": "easyad-1.0.8-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c8671ec17b42ecbce905f42f13af2de6", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16675, "upload_time": "2016-11-10T15:01:12", "url": "https://files.pythonhosted.org/packages/d1/42/655b91ddcd8ece9f4519ac0baf24de87d1f7b6c762264744e0645bfec3da/easyad-1.0.8-py2.py3-none-any.whl" } ], "1.0.9": [ { "comment_text": "", "digests": { "md5": "de3412c643b906e10708f6bb25ab31de", "sha256": "3ccb28a649d2010cfd9d822a9223b34c90e1c1be496a1158996d5bd6e4308cef" }, "downloads": -1, "filename": "easyad-1.0.9-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "de3412c643b906e10708f6bb25ab31de", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16432, "upload_time": "2017-02-24T19:09:08", "url": "https://files.pythonhosted.org/packages/47/bb/e2d215ec5e822d38290543a7d6e78b0be6ad004a592c47cead39e39d1a80/easyad-1.0.9-py2.py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "de3412c643b906e10708f6bb25ab31de", "sha256": "3ccb28a649d2010cfd9d822a9223b34c90e1c1be496a1158996d5bd6e4308cef" }, "downloads": -1, "filename": "easyad-1.0.9-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "de3412c643b906e10708f6bb25ab31de", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16432, "upload_time": "2017-02-24T19:09:08", "url": "https://files.pythonhosted.org/packages/47/bb/e2d215ec5e822d38290543a7d6e78b0be6ad004a592c47cead39e39d1a80/easyad-1.0.9-py2.py3-none-any.whl" } ] }