diff --git a/.gitignore b/.gitignore
index 1b2b4d16..2d923e99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,7 @@ __pycache__/
.ropeproject/
#django
local_settings.py
-
+Pipfile
media/
!media/keep
/CACHE/
@@ -43,3 +43,4 @@ secret-key
# to keep empty dirs
!.gitkeep
*.orig
+.vscode/settings.json
diff --git a/INSTALLATION.rst b/INSTALLATION.rst
index ee36b3ad..efa299f3 100644
--- a/INSTALLATION.rst
+++ b/INSTALLATION.rst
@@ -10,13 +10,35 @@ Requirements
Install
=======
+
+.. note::
+ lxml that is one of the dependency of dynamicweb couldn't
+ get build on Python 3.7 so, please use Python 3.5.
+
+
+First install packages from requirements.archlinux.txt or
+requirements.debian.txt based on your distribution.
+
+
The quick way:
``pip install -r requirements.txt``
Next find the dump.db file on stagging server. Path for the file is under the base application folder.
+or you can create one for yourself by running the following commands on dynamicweb server
+
+.. code:: sh
+
+ sudo su - postgres
+ pg_dump app > /tmp/postgres_db.bak
+ exit
+ cp /tmp/postgres_db.bak /root/postgres_db.bak
+
+Now, you can download this using sftp.
+
+
Install the postgresql server and import the database::
- ``psql -d app < dump.db``
+ ``psql -d app -U root < dump.db``
**No migration is needed after a clean install, and You are ready to start developing.**
@@ -25,9 +47,9 @@ Development
Project is separated in master branch and development branch, and feature branches.
Master branch is currently used on `Digital Glarus `_ and `Ungleich blog `_.
-If You are starting to create a new feature fork the github `repo `_ and branch the development branch.
+If You are starting to create a new feature fork the github `repo `_ and branch the development branch.
-After You have complited the task create a pull request and ask someone to review the code from other developers.
+After You have completed the task, create a pull request and ask someone to review the code from other developers.
**Cheat sheet for branching and forking**:
diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py
index fc971141..dbebc36e 100644
--- a/dynamicweb/settings/base.py
+++ b/dynamicweb/settings/base.py
@@ -10,7 +10,10 @@ import os
# dotenv
import dotenv
+import ldap
+
from django.utils.translation import ugettext_lazy as _
+from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
logger = logging.getLogger(__name__)
@@ -52,7 +55,7 @@ PROJECT_DIR = os.path.abspath(
)
# load .env file
-dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR))
+dotenv.load_dotenv("{0}/.env".format(PROJECT_DIR))
from multisite import SiteID
@@ -240,12 +243,14 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'app',
+ 'USER': 'root'
}
}
AUTHENTICATION_BACKENDS = (
+ 'utils.backend.MyLDAPBackend',
'guardian.backends.ObjectPermissionBackend',
- 'django.contrib.auth.backends.ModelBackend',
+
)
# Internationalization
@@ -721,6 +726,35 @@ X_FRAME_OPTIONS = ('SAMEORIGIN' if X_FRAME_OPTIONS_ALLOW_FROM_URI is None else
DEBUG = bool_env('DEBUG')
+
+# LDAP setup
+LDAP_SERVER = env('LDAP_SERVER')
+LDAP_ADMIN_DN = env('LDAP_ADMIN_DN')
+LDAP_ADMIN_PASSWORD = env('LDAP_ADMIN_PASSWORD')
+AUTH_LDAP_SERVER = env('LDAPSERVER')
+
+LDAP_CUSTOMER_DN = env('LDAP_CUSTOMER_DN')
+LDAP_CUSTOMER_GROUP_ID = int(env('LDAP_CUSTOMER_GROUP_ID'))
+LDAP_MAX_UID_FILE_PATH = os.environ.get('LDAP_MAX_UID_FILE_PATH',
+ os.path.join(os.path.abspath(os.path.dirname(__file__)), 'ldap_max_uid_file')
+)
+LDAP_DEFAULT_START_UID = int(env('LDAP_DEFAULT_START_UID'))
+
+# Search union over OUs
+search_base = env('LDAPSEARCH').split()
+search_base_ldap = [LDAPSearch(x, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") for x in search_base]
+AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*search_base_ldap)
+AUTH_LDAP_START_TLS = bool(os.environ.get('LDAP_USE_TLS', False))
+
+ENTIRE_SEARCH_BASE = env("ENTIRE_SEARCH_BASE")
+
+
+AUTH_LDAP_USER_ATTR_MAP = {
+ "first_name": "givenName",
+ "last_name": "sn",
+ "email": "mail"
+}
+
READ_VM_REALM = env('READ_VM_REALM')
AUTH_NAME = env('AUTH_NAME')
AUTH_SEED = env('AUTH_SEED')
diff --git a/dynamicweb/settings/ldap_max_uid_file b/dynamicweb/settings/ldap_max_uid_file
new file mode 100644
index 00000000..9c1cfb87
--- /dev/null
+++ b/dynamicweb/settings/ldap_max_uid_file
@@ -0,0 +1 @@
+10173
\ No newline at end of file
diff --git a/hosting/views.py b/hosting/views.py
index 21ede03e..7ee1b93b 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -57,6 +57,8 @@ from utils.hosting_utils import (
from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
+from utils.ldap_manager import LdapManager
+
from utils.views import (
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin,
ResendActivationLinkViewMixin
@@ -394,9 +396,12 @@ class PasswordResetConfirmView(HostingContextMixin,
if user is not None and default_token_generator.check_token(user,
token):
if form.is_valid():
+ ldap_manager = LdapManager()
new_password = form.cleaned_data['new_password2']
+ user.create_ldap_account()
user.set_password(new_password)
user.save()
+ ldap_manager.change_password(user.username, user.password)
messages.success(request, _('Password has been reset.'))
# Change opennebula password
diff --git a/membership/migrations/0011_customuser_username.py b/membership/migrations/0011_customuser_username.py
new file mode 100644
index 00000000..21a9cc14
--- /dev/null
+++ b/membership/migrations/0011_customuser_username.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-12-10 10:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('membership', '0010_customuser_import_stripe_bill_remark'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='customuser',
+ name='username',
+ field=models.CharField(max_length=50, null=True),
+ ),
+ ]
diff --git a/membership/migrations/0012_auto_20191210_1141.py b/membership/migrations/0012_auto_20191210_1141.py
new file mode 100644
index 00000000..7a64373a
--- /dev/null
+++ b/membership/migrations/0012_auto_20191210_1141.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-12-10 11:41
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('membership', '0011_customuser_username'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='customuser',
+ name='username',
+ field=models.CharField(max_length=50, null=True, unique=True),
+ ),
+ ]
diff --git a/membership/migrations/0013_customuser_in_ldap.py b/membership/migrations/0013_customuser_in_ldap.py
new file mode 100644
index 00000000..81cd2fd7
--- /dev/null
+++ b/membership/migrations/0013_customuser_in_ldap.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-12-10 15:30
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('membership', '0012_auto_20191210_1141'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='customuser',
+ name='in_ldap',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/membership/migrations/0014_remove_customuser_in_ldap.py b/membership/migrations/0014_remove_customuser_in_ldap.py
new file mode 100644
index 00000000..af594e1f
--- /dev/null
+++ b/membership/migrations/0014_remove_customuser_in_ldap.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-12-10 15:36
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('membership', '0013_customuser_in_ldap'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='customuser',
+ name='in_ldap',
+ ),
+ ]
diff --git a/membership/migrations/0015_customuser_in_ldap.py b/membership/migrations/0015_customuser_in_ldap.py
new file mode 100644
index 00000000..39c3384b
--- /dev/null
+++ b/membership/migrations/0015_customuser_in_ldap.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-12-10 17:05
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('membership', '0014_remove_customuser_in_ldap'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='customuser',
+ name='in_ldap',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/membership/models.py b/membership/models.py
index df5a5326..99180715 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -1,5 +1,6 @@
-from datetime import datetime
+import logging
+from datetime import datetime
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, \
@@ -7,13 +8,16 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, \
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.core.validators import RegexValidator
-from django.db import models
+from django.db import models, IntegrityError
from django.utils.crypto import get_random_string
from django.utils.translation import ugettext_lazy as _
from utils.mailer import BaseEmail
from utils.mailer import DigitalGlarusRegistrationMailer
from utils.stripe_utils import StripeUtils
+from utils.ldap_manager import LdapManager
+
+logger = logging.getLogger(__name__)
REGISTRATION_MESSAGE = {'subject': "Validation mail",
'message': 'Please validate Your account under this link '
@@ -42,6 +46,7 @@ class MyUserManager(BaseUserManager):
user.is_admin = False
user.set_password(password)
user.save(using=self._db)
+ user.create_ldap_account()
return user
def create_superuser(self, email, name, password):
@@ -63,13 +68,43 @@ def get_validation_slug():
return make_password(None)
+def get_first_and_last_name(full_name):
+ first_name, *last_name = full_name.split(" ")
+ first_name = first_name
+ last_name = " ".join(last_name)
+ return first_name, last_name
+
+
+def assign_username(user):
+ if not user.username:
+ first_name, last_name = get_first_and_last_name(user.name)
+ user.username = first_name.lower() + last_name.lower()
+ user.username = "".join(user.username.split())
+ try:
+ user.save()
+ except IntegrityError:
+ try:
+ user.username = user.username + str(user.id)
+ user.save()
+ except IntegrityError:
+ while True:
+ user.username = user.username + str(random.randint(0, 2 ** 50))
+ try:
+ user.save()
+ except IntegrityError:
+ continue
+ else:
+ break
+
+
class CustomUser(AbstractBaseUser, PermissionsMixin):
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
site = models.ForeignKey(Site, default=1)
name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
-
+ username = models.CharField(max_length=50, unique=True, null=True)
validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0)
+ in_ldap = models.BooleanField(default=False)
# By default, we initialize the validation_slug with appropriate value
# This is required for User(page) admin
validation_slug = models.CharField(
@@ -164,6 +199,34 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
# The user is identified by their email address
return self.email
+ def create_ldap_account(self):
+ # create ldap account for user if it does not exists already.
+ if self.in_ldap:
+ return
+
+ assign_username(self)
+ ldap_manager = LdapManager()
+ try:
+ user_exists_in_ldap, entries = ldap_manager.check_user_exists(
+ uid=self.username,
+ attributes=['uid', 'givenName', 'sn', 'mail', 'userPassword'],
+ search_base=settings.ENTIRE_SEARCH_BASE,
+ search_attr='uid'
+ )
+ except Exception:
+ logger.exception("Exception occur while searching for user in LDAP")
+ else:
+ if not user_exists_in_ldap:
+ # IF no ldap account
+ first_name, last_name = get_first_and_last_name(self.name)
+ if not last_name:
+ last_name = first_name
+
+ ldap_manager.create_user(self.username, password=self.password,
+ firstname=first_name, lastname=last_name,
+ email=self.email)
+ self.in_ldap = True
+ self.save()
def __str__(self): # __unicode__ on Python 2
return self.email
diff --git a/requirements.archlinux.txt b/requirements.archlinux.txt
index b4cab6e4..15184f0d 100644
--- a/requirements.archlinux.txt
+++ b/requirements.archlinux.txt
@@ -1 +1,2 @@
+base-devel
libmemcached
diff --git a/utils/backend.py b/utils/backend.py
new file mode 100644
index 00000000..f67763ca
--- /dev/null
+++ b/utils/backend.py
@@ -0,0 +1,73 @@
+
+import logging
+
+from membership.models import CustomUser
+logger = logging.getLogger(__name__)
+
+class MyLDAPBackend(object):
+ def authenticate(self, email, password):
+ try:
+ user = CustomUser.objects.get(email=email)
+ except CustomUser.DoesNotExist:
+ # User does not exists in Database
+ return None
+ else:
+ user.create_ldap_account()
+ if user.check_password(password):
+ return user
+ else:
+ return None
+
+ # # User exists in Database
+ # user.create_ldap_account()
+ # # User does not have a username
+ # if not user.username:
+ # assign_username(user)
+ #
+ # ldap_manager = LdapManager()
+ # try:
+ # user_exists_in_ldap, entries = ldap_manager.check_user_exists(
+ # uid=user.username,
+ # attributes=['uid', 'givenName', 'sn', 'mail', 'userPassword'],
+ # search_base=settings.ENTIRE_SEARCH_BASE,
+ # search_attr='uid'
+ # )
+ # except Exception:
+ # logger.exception("Exception occur while searching for user in LDAP")
+ # else:
+ # ph = PasswordHasher()
+ # if user_exists_in_ldap:
+ # # User Exists in LDAP
+ # password_hash_from_ldap = entries[0]["userPassword"].value
+ # try:
+ # ph.verify(password_hash_from_ldap, password)
+ # except Exception:
+ # # Incorrect LDAP Password
+ # return None
+ # else:
+ # # Correct LDAP Password
+ # return user
+ # else:
+ # # User does not exists in LDAP
+ # if user.check_password(password):
+ # # Password is correct as per database
+ # first_name, last_name = get_first_and_last_name(user.name)
+ # if not last_name:
+ # last_name = first_name
+ #
+ # ldap_manager.create_user(user.username, password=ph.hash(password),
+ # firstname=first_name, lastname=last_name,
+ # email=user.email)
+ # user.password = "IN_LDAP"
+ # user.save()
+ # return user
+ # else:
+ # # Incorrect Password
+ # print("Incorrect password")
+ # return None
+
+ def get_user(self, user_id):
+ try:
+ return CustomUser.objects.get(pk=user_id)
+ except CustomUser.DoesNotExist:
+ return None
diff --git a/utils/ldap_manager.py b/utils/ldap_manager.py
new file mode 100644
index 00000000..602bf6f2
--- /dev/null
+++ b/utils/ldap_manager.py
@@ -0,0 +1,279 @@
+import base64
+import hashlib
+import random
+import ldap3
+import logging
+
+from django.conf import settings
+
+logger = logging.getLogger(__name__)
+
+
+class LdapManager:
+ __instance = None
+
+ def __new__(cls):
+ if LdapManager.__instance is None:
+ LdapManager.__instance = object.__new__(cls)
+ return LdapManager.__instance
+
+ def __init__(self):
+ """
+ Initialize the LDAP subsystem.
+ """
+ self.rng = random.SystemRandom()
+ self.server = ldap3.Server(settings.AUTH_LDAP_SERVER)
+
+ def get_admin_conn(self):
+ """
+ Return a bound :class:`ldap3.Connection` instance which has write
+ permissions on the dn in which the user accounts reside.
+ """
+ conn = self.get_conn(user=settings.LDAP_ADMIN_DN,
+ password=settings.LDAP_ADMIN_PASSWORD,
+ raise_exceptions=True)
+ conn.bind()
+ return conn
+
+ def get_conn(self, **kwargs):
+ """
+ Return an unbound :class:`ldap3.Connection` which talks to the configured
+ LDAP server.
+
+ The *kwargs* are passed to the constructor of :class:`ldap3.Connection` and
+ can be used to set *user*, *password* and other useful arguments.
+ """
+ return ldap3.Connection(self.server, **kwargs)
+
+ def _ssha_password(self, password):
+ """
+ Apply the SSHA password hashing scheme to the given *password*.
+ *password* must be a :class:`bytes` object, containing the utf-8
+ encoded password.
+
+ Return a :class:`bytes` object containing ``ascii``-compatible data
+ which can be used as LDAP value, e.g. after armoring it once more using
+ base64 or decoding it to unicode from ``ascii``.
+ """
+ SALT_BYTES = 15
+
+ sha1 = hashlib.sha1()
+ salt = self.rng.getrandbits(SALT_BYTES * 8).to_bytes(SALT_BYTES,
+ "little")
+ sha1.update(password)
+ sha1.update(salt)
+
+ digest = sha1.digest()
+ passwd = b"{SSHA}" + base64.b64encode(digest + salt)
+ return passwd
+
+ def create_user(self, user, password, firstname, lastname, email):
+ conn = self.get_admin_conn()
+ uidNumber = self._get_max_uid() + 1
+ logger.debug("uidNumber={uidNumber}".format(uidNumber=uidNumber))
+ user_exists = True
+ while user_exists:
+ user_exists, _ = self.check_user_exists(
+ "",
+ '(&(objectClass=inetOrgPerson)(objectClass=posixAccount)'
+ '(objectClass=top)(uidNumber={uidNumber}))'.format(
+ uidNumber=uidNumber
+ )
+ )
+ if user_exists:
+ logger.debug(
+ "{uid} exists. Trying next.".format(uid=uidNumber)
+ )
+ uidNumber += 1
+ logger.debug("{uid} does not exist. Using it".format(uid=uidNumber))
+ self._set_max_uid(uidNumber)
+ try:
+ uid = user
+ conn.add("uid={uid},{customer_dn}".format(
+ uid=uid, customer_dn=settings.LDAP_CUSTOMER_DN
+ ),
+ ["inetOrgPerson", "posixAccount", "ldapPublickey"],
+ {
+ "uid": [uid],
+ "sn": [lastname],
+ "givenName": [firstname],
+ "cn": [uid],
+ "displayName": ["{} {}".format(firstname, lastname)],
+ "uidNumber": [str(uidNumber)],
+ "gidNumber": [str(settings.LDAP_CUSTOMER_GROUP_ID)],
+ "loginShell": ["/bin/bash"],
+ "homeDirectory": ["/home/{}".format(user)],
+ "mail": email,
+ "userPassword": [password]
+ }
+ )
+ logger.debug('Created user %s %s' % (user.encode('utf-8'),
+ uidNumber))
+ except Exception as ex:
+ logger.debug('Could not create user %s' % user.encode('utf-8'))
+ logger.error("Exception: " + str(ex))
+ raise Exception(ex)
+ finally:
+ conn.unbind()
+
+ def change_password(self, uid, new_password):
+ """
+ Changes the password of the user identified by user_dn
+
+ :param uid: str The uid that identifies the user
+ :param new_password: str The new password string
+ :return: True if password was changed successfully False otherwise
+ """
+ conn = self.get_admin_conn()
+
+ # Make sure the user exists first to change his/her details
+ user_exists, entries = self.check_user_exists(
+ uid=uid,
+ search_base=settings.ENTIRE_SEARCH_BASE
+ )
+ return_val = False
+ if user_exists:
+ try:
+ return_val = conn.modify(
+ entries[0].entry_dn,
+ {
+ "userpassword": (
+ ldap3.MODIFY_REPLACE,
+ [new_password]
+ )
+ }
+ )
+ except Exception as ex:
+ logger.error("Exception: " + str(ex))
+ else:
+ logger.error("User {} not found".format(uid))
+
+ conn.unbind()
+ return return_val
+
+ def change_user_details(self, uid, details):
+ """
+ Updates the user details as per given values in kwargs of the user
+ identified by user_dn.
+
+ Assumes that all attributes passed in kwargs are valid.
+
+ :param uid: str The uid that identifies the user
+ :param details: dict A dictionary containing the new values
+ :return: True if user details were updated successfully False otherwise
+ """
+ conn = self.get_admin_conn()
+
+ # Make sure the user exists first to change his/her details
+ user_exists, entries = self.check_user_exists(
+ uid=uid,
+ search_base=settings.ENTIRE_SEARCH_BASE
+ )
+
+ return_val = False
+ if user_exists:
+ details_dict = {k: (ldap3.MODIFY_REPLACE, [v.encode("utf-8")]) for
+ k, v in details.items()}
+ try:
+ return_val = conn.modify(entries[0].entry_dn, details_dict)
+ msg = "success"
+ except Exception as ex:
+ msg = str(ex)
+ logger.error("Exception: " + msg)
+ finally:
+ conn.unbind()
+ else:
+ msg = "User {} not found".format(uid)
+ logger.error(msg)
+ conn.unbind()
+ return return_val, msg
+
+ def check_user_exists(self, uid, search_filter="", attributes=None,
+ search_base=settings.LDAP_CUSTOMER_DN, search_attr="uid"):
+ """
+ Check if the user with the given uid exists in the customer group.
+
+ :param uid: str representing the user
+ :param search_filter: str representing the filter condition to find
+ users. If its empty, the search finds the user with
+ the given uid.
+ :param attributes: list A list of str representing all the attributes
+ to be obtained in the result entries
+ :param search_base: str
+ :return: tuple (bool, [ldap3.abstract.entry.Entry ..])
+ A bool indicating if the user exists
+ A list of all entries obtained in the search
+ """
+ conn = self.get_admin_conn()
+ entries = []
+ try:
+ result = conn.search(
+ search_base=search_base,
+ search_filter=search_filter if len(search_filter) > 0 else
+ '(uid={uid})'.format(uid=uid),
+ attributes=attributes
+ )
+ entries = conn.entries
+ finally:
+ conn.unbind()
+ return result, entries
+
+ def delete_user(self, uid):
+ """
+ Deletes the user with the given uid from ldap
+
+ :param uid: str representing the user
+ :return: True if the delete was successful False otherwise
+ """
+ conn = self.get_admin_conn()
+ try:
+ return_val = conn.delete(
+ ("uid={uid}," + settings.LDAP_CUSTOMER_DN).format(uid=uid),
+ )
+ msg = "success"
+ except Exception as ex:
+ msg = str(ex)
+ logger.error("Exception: " + msg)
+ return_val = False
+ finally:
+ conn.unbind()
+ return return_val, msg
+
+ def _set_max_uid(self, max_uid):
+ """
+ a utility function to save max_uid value to a file
+
+ :param max_uid: an integer representing the max uid
+ :return:
+ """
+ with open(settings.LDAP_MAX_UID_FILE_PATH, 'w+') as handler:
+ handler.write(str(max_uid))
+
+ def _get_max_uid(self):
+ """
+ A utility function to read the max uid value that was previously set
+
+ :return: An integer representing the max uid value that was previously
+ set
+ """
+ try:
+ with open(settings.LDAP_MAX_UID_FILE_PATH, 'r+') as handler:
+ try:
+ return_value = int(handler.read())
+ except ValueError as ve:
+ logger.error(
+ "Error reading int value from {}. {}"
+ "Returning default value {} instead".format(
+ settings.LDAP_MAX_UID_PATH,
+ str(ve),
+ settings.LDAP_DEFAULT_START_UID
+ )
+ )
+ return_value = settings.LDAP_DEFAULT_START_UID
+ return return_value
+ except FileNotFoundError as fnfe:
+ logger.error("File not found : " + str(fnfe))
+ return_value = settings.LDAP_DEFAULT_START_UID
+ logger.error("So, returning UID={}".format(return_value))
+ return return_value
+