<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lars Quentin</title>
    <description>The latest articles on DEV Community by Lars Quentin (@lquenti).</description>
    <link>https://dev.to/lquenti</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3680%2F21225159.jpeg</url>
      <title>DEV Community: Lars Quentin</title>
      <link>https://dev.to/lquenti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lquenti"/>
    <language>en</language>
    <item>
      <title>Use XQuery for HTML (Web Crawling)</title>
      <dc:creator>Lars Quentin</dc:creator>
      <pubDate>Fri, 06 Dec 2024 16:02:58 +0000</pubDate>
      <link>https://dev.to/lquenti/use-xquery-for-processing-html-web-crawling-2plb</link>
      <guid>https://dev.to/lquenti/use-xquery-for-processing-html-web-crawling-2plb</guid>
      <description>&lt;p&gt;&lt;strong&gt;This is a very very niche post. If you don't know why this is a pain point, don't waste your time reading this.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;XQuery is a great language for high level XML processing, providing a fully turing complete declarative language leveraging XPath. Unfortunately, it is not used often.&lt;/p&gt;

&lt;p&gt;My personal take is that this is the case because most HTML out there is not XML compliant, mostly because of tags that are never closed (such as &lt;code&gt;&amp;lt;link ...&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;link .../&amp;gt;&lt;/code&gt;). Thus your Saxon/BaseX parser will fail.&lt;/p&gt;

&lt;p&gt;The solution is &lt;a href="http://vrici.lojban.org/~cowan/tagsoup/" rel="noopener noreferrer"&gt;TagSoup&lt;/a&gt;, which provides a SAX-compliant HTML parser, pretending that it just parses XML. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BaseX &lt;a href="https://docs.basex.org/main/Parsers#html_parser" rel="noopener noreferrer"&gt;just uses it for you if you have it in Path!&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Saxon provides &lt;a href="https://www.saxonica.com/html/documentation9.4/extensions/functions/parse-html.html" rel="noopener noreferrer"&gt;&lt;code&gt;saxon:parse-html&lt;/code&gt; if TagSoup is in the classpath&lt;/a&gt;, which can be used after fetching with &lt;a href="https://www.saxonica.com/html/documentation12/functions/fn/unparsed-text.html" rel="noopener noreferrer"&gt;&lt;code&gt;fn:unparsed-text&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that, you can now do actual web crawling! The rest is just plain XQuery.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hack: Forcing Puppet to apt update</title>
      <dc:creator>Lars Quentin</dc:creator>
      <pubDate>Fri, 09 Apr 2021 16:50:58 +0000</pubDate>
      <link>https://dev.to/lquenti/forcing-puppet-to-apt-update-2lb3</link>
      <guid>https://dev.to/lquenti/forcing-puppet-to-apt-update-2lb3</guid>
      <description>&lt;p&gt;Short hack: As seen in &lt;a href="https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L11" rel="noopener noreferrer"&gt;m&lt;/a&gt; &lt;a href="https://github.com/docker-library/httpd/blob/master/2.4/Dockerfile#L19" rel="noopener noreferrer"&gt;a&lt;/a&gt; &lt;a href="https://github.com/nginxinc/docker-nginx/blob/master/Dockerfile-debian.template#L28" rel="noopener noreferrer"&gt;n&lt;/a&gt; &lt;a href="https://github.com/docker-library/redis/blob/master/Dockerfile.template#L13" rel="noopener noreferrer"&gt;y&lt;/a&gt; Dockerfiles, the &lt;code&gt;apt&lt;/code&gt; cache is located at &lt;code&gt;/var/lib/apt/lists&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Therefore, we can use it's existence to create an &lt;a href="https://puppet.com/docs/pe/2019.2/understanding_idempotency.html" rel="noopener noreferrer"&gt;idempotent&lt;/a&gt; &lt;code&gt;exec&lt;/code&gt;-Ressouce!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exec { 'apt update':
  # Only if the update files dont exist
  unless =&amp;gt; '[ "$(ls -A /var/lib/apt/lists)" ]',
  # /bin is needed for bash
  # /usr/bin is needed for [
  path =&amp;gt; '/bin:/usr/bin',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now whenever you need to have the &lt;code&gt;apt&lt;/code&gt; cache first, just do something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package { 'your-package':
  ensure =&amp;gt; 'present',
  require =&amp;gt; Exec['apt update']
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy as 🥧&lt;/p&gt;

</description>
      <category>puppet</category>
    </item>
    <item>
      <title>Using Grub without Linux for Windows 7/10 Dual Boot Configuration</title>
      <dc:creator>Lars Quentin</dc:creator>
      <pubDate>Fri, 21 Aug 2020 20:50:50 +0000</pubDate>
      <link>https://dev.to/lquenti/using-grub-without-linux-for-windows-7-10-dual-boot-configuration-45pf</link>
      <guid>https://dev.to/lquenti/using-grub-without-linux-for-windows-7-10-dual-boot-configuration-45pf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I throw together things until it works&lt;br&gt;
-- Rasmus Lerdorf, Creator of PHP&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TL;DR at the bottom&lt;/p&gt;




&lt;p&gt;This is part 1 on a 2 part series about manual grub configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing Standalone Grub for Windows 7/10 Dual Boot Configuration&lt;/li&gt;
&lt;li&gt;The Absolute Minimum Every Linux User Absolutely, Positively Must Know About grub.cfg (No Excuses!) (coming soon®)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a dual boot with

&lt;ul&gt;
&lt;li&gt;A hard drive with Windows 7&lt;/li&gt;
&lt;li&gt;A SSD with Windows 10&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;I am not allowed to edit the &lt;a href="https://en.wikipedia.org/wiki/EasyBCD" rel="noopener noreferrer"&gt;BCD&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;I am not allowed to install any other OS&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;The Idea: Installing Grub with 2 Windows bootloader entries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Preparation is Half the Battle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Making Space for the boot Partition
&lt;/h3&gt;

&lt;p&gt;Since we shrink NTFS, we use Windows for that.&lt;/p&gt;

&lt;p&gt;First, press &lt;code&gt;&amp;lt;Win-r&amp;gt;&lt;/code&gt; and run &lt;code&gt;compmgmt.msc&lt;/code&gt; and use the "Disk Management" utility.&lt;/p&gt;

&lt;p&gt;Right click on the partition you want to shrink and make at least 200MB space for grub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpr311bda8cwpwahzesbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpr311bda8cwpwahzesbd.png" alt="Shink that f*cker" width="440" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you made some free space, right click the free space and create a new partition using "New Simple Volume". Which settings you select doesn't matter since we will overwrite it later.&lt;/p&gt;

&lt;p&gt;After that, the last preparation step is to&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a neat Ubuntu Stick
&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://rufus.ie/" rel="noopener noreferrer"&gt;Just use rufus.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Grub
&lt;/h2&gt;

&lt;p&gt;After you booted up, grab a root shell, and find out which the new partition is (for example by listing all partitions via &lt;code&gt;fdisk -l&lt;/code&gt; and comparing size. &lt;a href="https://en.wikipedia.org/wiki/Loop_device" rel="noopener noreferrer"&gt;You can ignore all &lt;code&gt;/dev/loopXX&lt;/code&gt;.&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Now we format it with a grub compatible file system. From now on, the new partiton is &lt;code&gt;/dev/sda2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(for simplicity, I assume bios/mbr from now on)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkfs.ext4 /dev/sda2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, we mount it, create a boot directory and try to install grub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mount /dev/sda2 /mnt &amp;amp;&amp;amp; mkdir /mnt/boot
# The --boot-directory is needed since we don't manipulate
# our own bootloader, therefore we don't have the default
# location /boot
grub-install --boot-directory=/mnt/boot /dev/sda1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that didn't work, just force it, we have to configure it manually anyways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grub-install --force --boot-directory=/mnt/boot /dev/sda1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can't be that easy! And it isn't!!!! When you try to generate the config with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grub-mkconfig -o /mnt/boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it should cry about something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/sbin/grub-probe:error:failed to get canonical path of /cow.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens because &lt;code&gt;grub-probe&lt;/code&gt;, the tool called by the &lt;code&gt;grub-mkconfig&lt;/code&gt; script, can't handle ubuntu's live system root.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvise, Adapt, Overcome
&lt;/h2&gt;

&lt;p&gt;But that's no problem! Writing one's own minimal &lt;code&gt;grub.cfg&lt;/code&gt; is easier than you'd expect! &lt;/p&gt;

&lt;p&gt;First, find out which UUID's your &lt;code&gt;C:\&lt;/code&gt; partitions have. Again, just compare the size and file system with &lt;code&gt;fdisk -l&lt;/code&gt; and throw the &lt;code&gt;/dev/sdxx&lt;/code&gt; into &lt;code&gt;blkid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's start writing a &lt;code&gt;grub.cfg&lt;/code&gt; by opening your favorite editor&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano -w /mnt/boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We just have to do 2 things. First, we set some sane defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Set first as default
set default="0"

# Set timeout for 10 seconds that we have enough time
set timeout=10

# sane IO
terminal_input console
terminal_output gfxterm

# minimalistic color sheme
set menu_color_normal=white/black
set menu_color_highlight=black/light-gray
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have to create the entries. I'll comment as I go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Here, the "Windows 10" is just the representative name
# it can be chosen as you want to
menuentry "Windows 10" --class windows --class os {
        # insmod ntfs loads ntfs support
    insmod ntfs
        # finds the right partition by the UUID you found earlier
    search --no-floppy --set=root --fs-uuid &amp;lt;YOUR UUID&amp;gt;
        # Starts the windows NT loader
    ntldr /bootmgr
}

# Analogous for your other windows
menuentry "Windows 7" --class windows --class os {
    insmod ntfs
    search --no-floppy --set=root --fs-uuid &amp;lt;YOUR UUID&amp;gt;
    ntldr /bootmgr
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, just unmount everything, change the boot order if necessary and et voila, you solved the problem!&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR Code
&lt;/h2&gt;

&lt;p&gt;Minimal &lt;code&gt;grub.cfg&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set default="0"
set timeout=10
terminal_input console
terminal_output gfxterm
set menu_color_normal=white/black
set menu_color_highlight=black/light-gray
menuentry "Windows 10" --class windows --class os {
    insmod ntfs
    search --no-floppy --set=root --fs-uuid &amp;lt;YOUR UUID&amp;gt;
    ntldr /bootmgr
}
menuentry "Windows 7" --class windows --class os {
    insmod ntfs
    search --no-floppy --set=root --fs-uuid &amp;lt;YOUR UUID&amp;gt;
    ntldr /bootmgr
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thanks for reading and &lt;a href="http://atagunov.blogspot.com/2012/04/installing-grub2-without-linux.html" rel="noopener noreferrer"&gt;thanks to Anton Tagunov for his blog post on the topic&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>grub</category>
      <category>linux</category>
      <category>windows</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Dynamic Group based LDAP authentication with django and RegEx</title>
      <dc:creator>Lars Quentin</dc:creator>
      <pubDate>Sun, 01 Mar 2020 20:11:22 +0000</pubDate>
      <link>https://dev.to/lquenti/dynamic-group-based-ldap-authentication-with-django-and-regex-1h4p</link>
      <guid>https://dev.to/lquenti/dynamic-group-based-ldap-authentication-with-django-and-regex-1h4p</guid>
      <description>&lt;p&gt;I had the following problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication over Active Directory through LDAP&lt;/li&gt;
&lt;li&gt;just certain groups with a specific suffix were allowed to login&lt;/li&gt;
&lt;li&gt;There was no way to know how many of those groups exist&lt;/li&gt;
&lt;li&gt;Later views depended on those groups, therefore they had to be mapped to django groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;: If you just want the full code, feel free to jump to the end, but at least leave a upvote. :D&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites (You already know them)
&lt;/h2&gt;

&lt;p&gt;Besides &lt;code&gt;django&lt;/code&gt;, you need &lt;code&gt;django-auth-ldap&lt;/code&gt; as well&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install django-auth-ldap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with that installed, we are ready to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing our own basic LDAP Backend
&lt;/h2&gt;

&lt;p&gt;At first, let's create our own Backend, by extending the default LDAPBackend from &lt;code&gt;django-auth-ldap&lt;/code&gt;. &lt;br&gt;
For the official documentation, see &lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/custombehavior.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;name_of_main_application&amp;gt;/ldap.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupLDAPBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now before we do further customisation, let's just fill it with our parameters for the &lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/authentication.html" rel="noopener noreferrer"&gt;Search/Bind&lt;/a&gt; authentication:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;name_of_main_application&amp;gt;/ldap.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ldap&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.backend&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LDAPBackend&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LDAPSearch&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupLDAPBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;default_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# All those settings are overwriting base class values
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SERVER_URI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ldaps://url.to.our.dc.domain.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CACHE_TIMEOUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# Those settings should probably be overwritten by the settings.py
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BIND_DN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BIND_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USER_SEARCH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OU=&amp;lt;OU_OF_USER_LOGGING_IN&amp;gt;, OU=..., DC=..., DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# If you know, that all your users logging in are on that 
&lt;/span&gt;      &lt;span class="c1"&gt;# exact ou depth specified above, you can get better performance
&lt;/span&gt;      &lt;span class="c1"&gt;# by using ldap.SCOPE_BASE or for that depth and its direct 
&lt;/span&gt;      &lt;span class="c1"&gt;# children ldap.SCOPE_ONELEVEL, see python-ldap documentation for
&lt;/span&gt;      &lt;span class="c1"&gt;# more.
&lt;/span&gt;      &lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCOPE_SUBTREE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(uid=%(user)s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those settings are the same as outside of the class with the &lt;code&gt;AUTH_LDAP&lt;/code&gt; Prefix.&lt;/p&gt;

&lt;p&gt;Here a short explaination of all those parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-server-uri" rel="noopener noreferrer"&gt;SERVER_URI&lt;/a&gt;: The URL to the domain controller&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-cache-timeout" rel="noopener noreferrer"&gt;CACHE_TIMEOUT&lt;/a&gt;: How long the data from the LDAP Server is cached. Here set to one hour.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-bind-dn" rel="noopener noreferrer"&gt;BIND_DN&lt;/a&gt;: The &lt;a href="https://ldapwiki.com/wiki/Distinguished%20Names" rel="noopener noreferrer"&gt;fully qualified Distinguished Name&lt;/a&gt;, which means that dc's/ou's cant be before the cn logging in mustn't be omitted. You'll see an example later.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/authentication.html#search-bind" rel="noopener noreferrer"&gt;USER_SEARCH&lt;/a&gt;: The search, which will be performed on in the authentication process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we can add the ldap authentication in the &lt;code&gt;settings.py&lt;/code&gt;, together with the other settings.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;name_of_main_application&amp;gt;/settings.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;AUTH_LDAP_BIND_DN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CN=&amp;lt;user_which_handles_all_binds&amp;gt;, OU=its_ou, ..., DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;AUTH_LDAP_BIND_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password_for_that_account&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;AUTHENTICATION_BACKENDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;name_of_main_application&amp;gt;.ldap.GroupLDAPBackend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;# And if wanted, the django one as well
&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.contrib.auth.backends.ModelBackend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, instead of hard coding (and even worse, committing) the credentials please export them into your linux environment and use &lt;code&gt;os.getenv(KEY)&lt;/code&gt;. This way, you don't save any critical information in your repository.&lt;/p&gt;

&lt;p&gt;Also, if you have any problems finding the credentials, you can enable logging as well with&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;name_of_main_application&amp;gt;/settings.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;LOGGING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disable_existing_loggers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handlers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logging.StreamHandler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loggers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django_auth_ldap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEBUG&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handlers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should already be enough to get the normal authentication working!&lt;br&gt;
Now we just have to implement the group-based authentication.&lt;br&gt;
Let's get right into the "fun"!&lt;/p&gt;
&lt;h2&gt;
  
  
  Extending our badass Backend with Group Authentication
&lt;/h2&gt;

&lt;p&gt;Let's say, we want to allow any user, which has a group with the suffix &lt;strong&gt;"django"&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;"some_group_django"&lt;/em&gt;: ✔️&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"admindjango"&lt;/em&gt;: ✔️&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"djangogrp"&lt;/em&gt;: ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For educational purposes, they all groups are located in&lt;br&gt;
&lt;code&gt;OU=Groups, DC=domain, DC=com&lt;/code&gt;&lt;br&gt;
and its sub organisational units.&lt;/p&gt;

&lt;p&gt;With that, we can extend our backend with our own &lt;em&gt;RegEx&lt;/em&gt;-Setting as well as &lt;a href="https://django-auth-ldap.readthedocs.io/en/latest/groups.html#finding-groups" rel="noopener noreferrer"&gt;GROUP_SEARCH and GROUP_TYPE&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ldap&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.backend&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LDAPBackend&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GroupOfNamesType&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupLDAPBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;default_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Our new settings
&lt;/span&gt;    &lt;span class="c1"&gt;# Lets call the RegEx GROUP_REGEX for simplicity
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP_REGEX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^*django$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP_SEARCH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OU=Groups, DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCOPE_SUBTREE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(cn=*django)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP_TYPE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GroupOfNamesType&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="c1"&gt;# All those settings are overwriting base class values
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SERVER_URI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ldaps://url.to.our.dc.domain.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CACHE_TIMEOUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# Those settings should probably be overwritten by the settings.py
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BIND_DN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BIND_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USER_SEARCH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OU=&amp;lt;OU_OF_USER_LOGGING_IN&amp;gt;, OU=..., DC=..., DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# If you know, that all your users logging in are on that 
&lt;/span&gt;      &lt;span class="c1"&gt;# exact ou depth specified above, you can get better performance
&lt;/span&gt;      &lt;span class="c1"&gt;# by using ldap.SCOPE_BASE or for that depth and its direct 
&lt;/span&gt;      &lt;span class="c1"&gt;# children ldap.SCOPE_ONELEVEL, see python-ldap documentation for
&lt;/span&gt;      &lt;span class="c1"&gt;# more.
&lt;/span&gt;      &lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCOPE_SUBTREE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(uid=%(user)s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can now override the &lt;code&gt;LDAPBackend.authenticate_ldap_user&lt;/code&gt; with our own version! &lt;/p&gt;

&lt;p&gt;I'll use some &lt;a href="https://docs.python.org/3/library/typing.html" rel="noopener noreferrer"&gt;type hinting&lt;/a&gt; here, since the Documentation for &lt;code&gt;_LDAPUser&lt;/code&gt; is more or less nonexisting so that you know where to find the source code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Just needed for type hinting lol
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.backend&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;_LDAPUser&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupLDAPBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;default_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate_ldap_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_LDAPUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# This is the default implemented authentication
&lt;/span&gt;    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ldap_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# If the authentication was denied, we have to return None
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;ldap_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ldap_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_names&lt;/span&gt;
    &lt;span class="n"&gt;ldap_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GROUP_REGEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mean, at least for me, that was way easier than expected.&lt;br&gt;
Lastly, we just have to create the django groups from the ldap ones.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating Django Groups on the fly!
&lt;/h2&gt;

&lt;p&gt;Luckily, django has the &lt;code&gt;get_or_create&lt;/code&gt; pattern, which makes things a lot easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupLDAPBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="bp"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_groups_and_assign_user_to_it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;django_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;was_created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;django_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! If it helped you or you have further questions, feel free to hook me up on twitter. &lt;br&gt;
Also, if there is a even neater way to implement it, please tell me and I'll correct it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The whole source code with some logging
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;name_of_main_application&amp;gt;/settings.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;AUTH_LDAP_BIND_DN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CN=&amp;lt;user_which_handles_all_binds&amp;gt;, OU=its_ou, ..., DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;AUTH_LDAP_BIND_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password_for_that_account&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;AUTHENTICATION_BACKENDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;name_of_main_application&amp;gt;.ldap.GroupLDAPBackend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;# And if wanted, the django one as well
&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.contrib.auth.backends.ModelBackend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;LOGGING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disable_existing_loggers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handlers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logging.StreamHandler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loggers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django_auth_ldap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEBUG&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handlers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;name_of_main_application&amp;gt;/ldap.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ldap&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.backend&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_LDAPUser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django_auth_ldap.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GroupOfNamesType&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupLDAPBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LDAPBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;default_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Our new settings
&lt;/span&gt;    &lt;span class="c1"&gt;# Lets call the RegEx GROUP_REGEX for simplicity
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP_REGEX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^*django$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP_SEARCH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OU=Groups, DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCOPE_SUBTREE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(cn=*django)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP_TYPE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GroupOfNamesType&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="c1"&gt;# All those settings are overwriting base class values
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SERVER_URI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ldaps://url.to.our.dc.domain.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CACHE_TIMEOUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# Those settings should probably be overwritten by the settings.py
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BIND_DN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BIND_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USER_SEARCH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LDAPSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OU=&amp;lt;OU_OF_USER_LOGGING_IN&amp;gt;, OU=..., DC=..., DC=domain, DC=com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# If you know, that all your users logging in are on that 
&lt;/span&gt;      &lt;span class="c1"&gt;# exact ou depth specified above, you can get better performance
&lt;/span&gt;      &lt;span class="c1"&gt;# by using ldap.SCOPE_BASE or for that depth and its direct 
&lt;/span&gt;      &lt;span class="c1"&gt;# children ldap.SCOPE_ONELEVEL, see python-ldap documentation for
&lt;/span&gt;      &lt;span class="c1"&gt;# more.
&lt;/span&gt;      &lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCOPE_SUBTREE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(uid=%(user)s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate_ldap_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_LDAPUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# This is the default implemented authentication
&lt;/span&gt;    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ldap_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# If the authentication was denied, we have to return None
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;ldap_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ldap_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_names&lt;/span&gt;
    &lt;span class="n"&gt;ldap_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GROUP_REGEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_groups_and_assign_user_to_it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_groups_and_assign_user_to_it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ldap_groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;django_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;was_created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;django_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>djangoauthldap</category>
      <category>ldap</category>
      <category>django</category>
      <category>activedirectory</category>
    </item>
  </channel>
</rss>
