{"id":185,"date":"2020-01-16T04:26:56","date_gmt":"2020-01-16T04:26:56","guid":{"rendered":"https:\/\/binaryopus.com\/?p=185"},"modified":"2020-01-16T04:26:56","modified_gmt":"2020-01-16T04:26:56","slug":"django-integration-with-active-directory","status":"publish","type":"post","link":"https:\/\/binaryopus.com\/?p=185","title":{"rendered":"Django integration with Active Directory"},"content":{"rendered":"\n<p>Recently I was working on an in house project for an internal employee portal. We chose to implement this new web service in Django and deploy it on a Red Hat instance. Django by default comes with its own authentication and authorization libraries but in this case we wanted to integrate with our existing Active Directory service. Fortunately, AD supports LDAP (Lightweight Directory Access Protocol). By using the Python LDAP library we were able to integrate it with our SSO solution.<\/p>\n\n\n\n<p>The documentation for the Django authentication backend is <a href=\"https:\/\/django-auth-ldap.readthedocs.io\/en\/latest\/index.html\">here<\/a> but the modifications I specifically used for Microsoft Active Directory are specified below<\/p>\n\n\n\n<p>To set this up, ensure the Django environment has the LDAP libraries installed. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install python-ldap\npip install D-auth-ldap<\/code><\/pre>\n\n\n\n<p>In settings.py import the LDAP libraries.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import ldap\nfrom django_auth_ldap.config import LDAPSearch, GroupOfNamesType<\/code><\/pre>\n\n\n\n<p>You&#8217;ll need to specify LDAP as an authentication backend. Furthermore, if you want to assign permissions to individual users or add users to groups in Django, you&#8217;ll need to also install the Django model backed as well.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#In settings.py\nimport ldap\n\nAUTHENTICATION_BACKENDS = [\n'django_auth_ldap.backend.LDAPBackend',\n'django.contrib.auth.backends.ModelBackend',\n}<\/code><\/pre>\n\n\n\n<p>In your django app settings.py file, set the following variables.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n#for a host of ldap.example.com\nAUTH_LDAP_SERVER_URI - 'ldap:\/\/ldap.example.com\nAUTH_LDAP_BIND_DN = 'CN=ldap_user,cn=Users,dc=example,dc=com'\n# For instace\n# AUTH_LDAP_BIND_DN = 'CN=ldap_user,cn=Users,dc=company,dc=local'\n\n# If you using password authentication with ldap, specify it with this settings\nAUTH_LDAP_BIND_PASSWORD = 'password'\n<\/code><\/pre>\n\n\n\n<p>Then you will need to specify the parameters for the LDAP user search when a user logs into the web service using Active directory credentials. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#For a domain of example.com\nAUTH_LDAP_USER_SEARCH = LDAPSearch(\n'dc=example,dc=com',ldap.SCOPE_SUBTREE,\n'sAMAccountName=$(user)s'<\/code><\/pre>\n\n\n\n<p>The sAMAccountName will be mapped to the user variable that is set during login.<\/p>\n\n\n\n<p>Next the ldap return values need to be mapped to fields in the Django user model. This is done with a dictionary specifying the django user object fields as keys and the LDAP field names as values.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nAUTH_LDAP_USER_ATTR_MAP = {\n 'username':'sAMAccountName',\n'first_name': 'givenName',\n'last_name': 'sn',\n'email': 'mail',\n#other fields as needed\n\n}\n# To ensure user object is updated each time on login\nAUTH_LDAP_ALWAYS_UPDATE_USER = True<\/code><\/pre>\n\n\n\n<p>You may decide to create your own user object if you want to define  other fields that aren&#8217;t included with the Django default user object. This can be done by declaring a new model that inherits the AbstractUser class. So in your models.py specify the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from django.contrib.auth.models import AbstractUser\n\nclass DerivedUser(AbstractUser):\n    #Define additional user fields here<\/code><\/pre>\n\n\n\n<p>Once you define a new user model, you will need to register it as the user model in your settings.py file<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AUTH_USER_MODEL = '&lt;app name>.&lt;user class>'\n# For instance WebApp.DerivedUser<\/code><\/pre>\n\n\n\n<p>You may also want to map Active Directory group memberships to user permissions in your django application. You&#8217;ll need to specify the type of group object returned by the LDAP search as well as the group search parameters in the settings.py file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AUTH_LDAP_GROUP_SEARCH = LDAPSearch(\n'ou=_groups,dc=example,dc=com', ldap.SCOPE_SUBTREE\n,\"(objectClass=group)\"\n)\n<\/code><\/pre>\n\n\n\n<p>Then map django permissions to a user&#8217;s groups.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AUTH_LDAP_USER_FLAGS_BY_GROUP = {\n'is_staff': 'CN=example_group,OU=_groups,DC=example,DC=com',\n)<\/code><\/pre>\n\n\n\n<p>Also if you want to require group membership before they can authenticate at all then specify a require group membership setting.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from django_auth_ldap.config import LDAPGroupQuery\nAUTH_LDAP_REQUIRE_GROUP = (\nLDAPGroupQuery('cn=group1,ou_groups,dc=example,dc=com'),\n)\n#You can also supply deny groups with the ~ before the group query. <\/code><\/pre>\n\n\n\n<p>For more information on groups check out.<\/p>\n\n\n\n<p><a href=\"https:\/\/django-auth-ldap.readthedocs.io\/en\/latest\/groups.html\">https:\/\/django-auth-ldap.readthedocs.io\/en\/latest\/groups.html<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I was working on an in house project for an internal employee portal. We chose to implement this new web service in Django and deploy it on a Red Hat instance. Django by default comes with its own authentication and authorization libraries but in this case we wanted to integrate with our existing Active [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[25,24],"tags":[],"_links":{"self":[{"href":"https:\/\/binaryopus.com\/index.php?rest_route=\/wp\/v2\/posts\/185"}],"collection":[{"href":"https:\/\/binaryopus.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/binaryopus.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/binaryopus.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/binaryopus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=185"}],"version-history":[{"count":2,"href":"https:\/\/binaryopus.com\/index.php?rest_route=\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":187,"href":"https:\/\/binaryopus.com\/index.php?rest_route=\/wp\/v2\/posts\/185\/revisions\/187"}],"wp:attachment":[{"href":"https:\/\/binaryopus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/binaryopus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/binaryopus.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}