Skip to content
This repository has been archived by the owner on Dec 9, 2022. It is now read-only.

Commit

Permalink
Merge 6c4e5bf into c798f07
Browse files Browse the repository at this point in the history
  • Loading branch information
ropable committed Jul 14, 2017
2 parents c798f07 + 6c4e5bf commit 88d9cee
Show file tree
Hide file tree
Showing 17 changed files with 879 additions and 468 deletions.
3 changes: 2 additions & 1 deletion oim_cms/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from mudmap.models import MudMap
from organisation.api import DepartmentUserResource, LocationResource, profile
from organisation.models import DepartmentUser, Location, OrgUnit, CostCentre
from registers.api import ITSystemResource, ITSystemHardwareResource
from registers.api import ITSystemResource, ITSystemHardwareResource, ITSystemEventResource
from registers.models import ITSystem
from tracking.api import EC2InstanceResource, FreshdeskTicketResource
from .utils import CSVDjangoResource
Expand Down Expand Up @@ -178,4 +178,5 @@ def create(self):
url(r'^profile/', profile, name='api_profile'),
url(r'^options/', include(OptionResource.urls())),
url(r'^whoami', WhoAmIResource.as_detail(), name='api_whoami'),
url(r'^events/', include(ITSystemEventResource.urls())),
]
446 changes: 23 additions & 423 deletions oim_cms/test_api.py

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions organisation/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class DepartmentUserAdmin(VersionAdmin):
readonly_fields = [
'username', 'email', 'org_data_pretty', 'ad_data_pretty',
'active', 'in_sync', 'ad_deleted', 'date_ad_updated',
'alesco_data_pretty', 'o365_licence', 'shared_account']
'alesco_data_pretty', 'o365_licence', 'shared_account',
'azure_guid']
fieldsets = (
('Email/username', {
'fields': ('email', 'username'),
Expand All @@ -82,9 +83,10 @@ class DepartmentUserAdmin(VersionAdmin):
'secondary_locations', 'working_hours', 'extra_data',
)
}),
('AD sync and HR data (read-only, except GUID)', {
('AD sync and HR data (read-only, except AD GUID)', {
'fields': (
'ad_guid',
'azure_guid',
'active', 'in_sync', 'ad_deleted', 'date_ad_updated',
'o365_licence', 'shared_account',
'org_data_pretty', 'ad_data_pretty', 'alesco_data_pretty',
Expand Down
135 changes: 100 additions & 35 deletions organisation/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


ACCOUNT_TYPE_DICT = dict(DepartmentUser.ACCOUNT_TYPE_CHOICES)
logger = logging.getLogger('ad_sync')
LOGGER = logging.getLogger('ad_sync')


def format_fileField(request, value):
Expand Down Expand Up @@ -191,37 +191,78 @@ def detail(self, guid):

@skip_prepare
def create(self):
"""Create view for a new DepartmentUserObject.
BUSINESS RULE: we call this endpoint from AD, and require a
complete request body that includes a GUID.
"""Call this endpoint from on-prem AD or from Azure AD.
Match either AD-object key values or Departmentuser field names.
"""
if 'ObjectGUID' not in self.data:
raise BadRequest('Missing ObjectGUID parameter')
user = DepartmentUser()
# Check for essential request params.
if 'EmailAddress' not in self.data and 'email' not in self.data:
raise BadRequest('Missing email parameter value')
if 'DisplayName' not in self.data and 'name' not in self.data:
raise BadRequest('Missing name parameter value')
if 'SamAccountName' not in self.data and 'username' not in self.data:
raise BadRequest('Missing account name parameter value')
# Required: email, name and sAMAccountName.
if 'EmailAddress' in self.data:
user.email = self.data['EmailAddress'].lower()
elif 'email' in self.data:
user.email = self.data['email'].lower()
if 'DisplayName' in self.data:
user.name = self.data['DisplayName']
elif 'name' in self.data:
user.name = self.data['name']
if 'SamAccountName' in self.data:
user.username = self.data['SamAccountName']
elif 'username' in self.data:
user.username = self.data['username']
# Optional fields.
if 'Enabled' in self.data:
user.active = self.data['Enabled']
elif 'active' in self.data:
user.active = self.data['active']
if 'ObjectGUID' in self.data:
user.ad_guid = self.data['ObjectGUID']
elif 'ad_guid' in self.data:
user.ad_guid = self.data['ad_guid']
if 'azure_guid' in self.data: # Exception to the if/elif rule.
user.azure_guid = self.data['azure_guid']
if 'Distinguishedname' in self.data:
user.ad_dn = self.data['DistinguishedName']
elif 'ad_dn' in self.data:
user.ad_dn = self.data['ad_dn']
if 'AccountExpirationDate' in self.data:
user.expiry_date = self.data['AccountExpirationDate']
elif 'expiry_date' in self.data:
user.expiry_date = self.data['expiry_date']
if 'Title' in self.data:
user.title = self.data['Title']
elif 'title' in self.data:
user.title = self.data['title']
if 'GivenName' in self.data:
user.given_name = self.data['GivenName']
elif 'given_name' in self.data:
user.given_name = self.data['given_name']
if 'Surname' in self.data:
user.surname = self.data['Surname']
elif 'given_name' in self.data:
user.surname = self.data['surname']
if 'Modified' in self.data:
user.date_ad_updated = self.data['Modified']
elif 'date_ad_updated' in self.data:
user.date_ad_updated = self.data['date_ad_updated']

try:
user = DepartmentUser.objects.get_or_create(
ad_guid=self.data['ObjectGUID'],
email=self.data['EmailAddress'].lower(),
ad_dn=self.data['DistinguishedName'],
username=self.data['SamAccountName'],
expiry_date=self.data['AccountExpirationDate'],
active=self.data['Enabled'],
name=self.data['DisplayName'],
title=self.data['Title'],
given_name=self.data['GivenName'],
surname=self.data['Surname'],
date_ad_updated=self.data['Modified'],
)[0]
user.save()
except Exception as e:
data = self.data
data['Error'] = repr(e)
logger.error(repr(e))
LOGGER.error(repr(e))
return self.formatters.format(self.request, {'Error': repr(e)})

# Serialise the newly-created DepartmentUser.
data = list(DepartmentUser.objects.filter(pk=user.pk).values(*self.VALUES_ARGS))[0]
logger.info('Created user {}'.format(user.email))
logger.info('{} '.format(self.formatters.format(self.request, data)))

LOGGER.info('Created user {}'.format(user.email))
LOGGER.info('{} '.format(self.formatters.format(self.request, data)))
return self.formatters.format(self.request, data)

def update(self, guid):
Expand All @@ -238,36 +279,60 @@ def update(self, guid):
raise BadRequest('Object not found')

try:
if 'ObjectGUID' in self.data and self.data['ObjectGUID']:
user.ad_guid = self.data['ObjectGUID']
if 'EmailAddress' in self.data and self.data['EmailAddress']:
user.email = self.data['EmailAddress'].lower()
if 'DistinguishedName' in self.data and self.data['DistinguishedName']:
user.ad_dn = self.data['DistinguishedName']
if 'email' in self.data and self.data['email']:
user.email = self.data['email'].lower()
if 'DisplayName' in self.data and self.data['DisplayName']:
user.name = self.data['DisplayName']
if 'name' in self.data and self.data['name']:
user.name = self.data['name']
if 'SamAccountName' in self.data and self.data['SamAccountName']:
user.username = self.data['SamAccountName']
if 'username' in self.data and self.data['username']:
user.username = self.data['username']
if 'ObjectGUID' in self.data and self.data['ObjectGUID']:
user.ad_guid = self.data['ObjectGUID']
if 'ad_guid' in self.data and self.data['ad_guid']:
user.ad_guid = self.data['ad_guid']
if 'DistinguishedName' in self.data and self.data['DistinguishedName']:
user.ad_dn = self.data['DistinguishedName']
if 'ad_dn' in self.data and self.data['ad_dn']:
user.ad_dn = self.data['ad_dn']
if 'AccountExpirationDate' in self.data and self.data['AccountExpirationDate']:
user.expiry_date = self.data['AccountExpirationDate']
if 'expiry_date' in self.data and self.data['expiry_date']:
user.expiry_date = self.data['expiry_date']
if 'Enabled' in self.data: # Boolean; don't only work on True!
user.active = self.data['Enabled']
if 'DisplayName' in self.data and self.data['DisplayName']:
user.name = self.data['DisplayName']
if 'active' in self.data: # Boolean; don't only work on True!
user.active = self.data['active']
if 'Title' in self.data and self.data['Title']:
user.title = self.data['Title']
if 'title' in self.data and self.data['title']:
user.title = self.data['title']
if 'GivenName' in self.data and self.data['GivenName']:
user.given_name = self.data['GivenName']
if 'given_name' in self.data and self.data['given_name']:
user.given_name = self.data['given_name']
if 'Surname' in self.data and self.data['Surname']:
user.surname = self.data['Surname']
if 'surname' in self.data and self.data['surname']:
user.surname = self.data['surname']
if 'Modified' in self.data and self.data['Modified']:
user.date_ad_updated = self.data['Modified']
if 'date_ad_updated' in self.data and self.data['date_ad_updated']:
user.date_ad_updated = self.data['date_ad_updated']
if 'o365_licence' in self.data: # Boolean; don't only work on True!
user.o365_licence = self.data['o365_licence']
if 'azure_guid' in self.data and self.data['azure_guid']:
user.azure_guid = self.data['azure_guid']
if 'Deleted' in self.data and self.data['Deleted']:
user.active = False
user.ad_deleted = True
user.ad_guid = ''
user.ad_guid, user.azure_guid = None, None
data = list(DepartmentUser.objects.filter(pk=user.pk).values(*self.VALUES_ARGS))[0]
logger.info('Set user {} as deleted in AD'.format(user.name))
LOGGER.info('Set user {} as deleted in AD'.format(user.name))
else:
user.ad_deleted = False
user.ad_data = self.data # Store the raw request data.
Expand All @@ -276,12 +341,12 @@ def update(self, guid):
except Exception as e:
data = self.data
data['Error'] = repr(e)
logger.error(repr(e))
LOGGER.error(repr(e))
return self.formatters.format(self.request, {'Error': repr(e)})

data = list(DepartmentUser.objects.filter(pk=user.pk).values(*self.VALUES_ARGS))[0]
logger.info('Updated user {}'.format(user.email))
logger.info('{}'.format(self.formatters.format(self.request, data)))
LOGGER.info('Updated user {}'.format(user.email))
LOGGER.info('{}'.format(self.formatters.format(self.request, data)))

return self.formatters.format(self.request, data)

Expand All @@ -297,7 +362,7 @@ def org_structure(self, sync_o365=False, exclude_populate_groups=False):
qs = qs.exclude(populate_primary_group=False)
structure = []
if sync_o365: # Exclude certain things from populating O365/AD
orgunits = OrgUnit.objects.filter(unit_type__in=[0, 1], sync_o365=True)
orgunits = OrgUnit.objects.filter(active=True, unit_type__in=[0, 1], sync_o365=True)
costcentres = []
locations = Location.objects.filter(active=True)
slocations = []
Expand Down
20 changes: 20 additions & 0 deletions organisation/migrations/0030_departmentuser_azure_guid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-12 08:54
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('organisation', '0029_auto_20170707_1224'),
]

operations = [
migrations.AddField(
model_name='departmentuser',
name='azure_guid',
field=models.CharField(blank=True, help_text='Azure AD GUID.', max_length=48, null=True, unique=True),
),
]
20 changes: 20 additions & 0 deletions organisation/migrations/0031_auto_20170713_1406.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-13 06:06
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('organisation', '0030_departmentuser_azure_guid'),
]

operations = [
migrations.AlterField(
model_name='departmentuser',
name='name',
field=models.CharField(help_text='Format: [Given name] [Surname]', max_length=128),
),
]
6 changes: 4 additions & 2 deletions organisation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class DepartmentUser(MPTTModel):
(2, 'Casual'),
(3, 'Other'),
)
# These fields are populated from Active Directory.

date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
cost_centre = models.ForeignKey(
Expand All @@ -79,6 +79,8 @@ class DepartmentUser(MPTTModel):
ad_guid = models.CharField(
max_length=48, unique=True, null=True, blank=True,
help_text='Locally stored GUID. This field must match GUID in the AD object for sync to be successful')
azure_guid = models.CharField(
max_length=48, unique=True, null=True, blank=True, help_text='Azure AD GUID.')
ad_dn = models.CharField(max_length=512, unique=True, null=True, blank=True, editable=False)
ad_data = JSONField(null=True, blank=True, editable=False)
org_data = JSONField(null=True, blank=True, editable=False)
Expand All @@ -89,7 +91,7 @@ class DepartmentUser(MPTTModel):
username = models.CharField(
max_length=128, editable=False, unique=True,
help_text='Pre-Windows 2000 login username.')
name = models.CharField(max_length=128, help_text='Format: Surname, Given name')
name = models.CharField(max_length=128, help_text='Format: [Given name] [Surname]')
given_name = models.CharField(
max_length=128, null=True,
help_text='Legal first name (matches birth certificate/password/etc.)')
Expand Down
Loading

0 comments on commit 88d9cee

Please sign in to comment.