private async Task CreatePerson(AdPerson adPerson)
        {
            if (adPerson.Username == null)
            {
                _logger.LogError($"AD person with OID: {adPerson.Oid} does not contain a username");
                return;
            }

            _logger.LogInformation($"Creating person with OID: {adPerson.Oid}");

            var userName = adPerson.Username.ToUpperInvariant();

            var(firstName, lastName) = GetAdPersonFirstAndLastName(adPerson);

            await _personRepository.AddPersonAsync(
                new Person(userName, adPerson.Email)
            {
                Oid               = adPerson.Oid,
                FirstName         = firstName,
                LastName          = lastName,
                MobilePhoneNumber = adPerson.MobileNumber?.Replace(" ", string.Empty),
                UpdatedAt         = DateTime.Now,
                CreatedById       = _personCreatedByCache.Id,
                UpdatedById       = _personCreatedByCache.Id,
                UpdatedByUser     = _personCreatedByCache.Username
            });
        }
        private async Task <Person?> FindAndUpdateAsync(AdPerson adPerson)
        {
            if (string.IsNullOrEmpty(adPerson.MobileNumber) ||
                ((string.IsNullOrEmpty(adPerson.GivenName) && string.IsNullOrEmpty(adPerson.Surname)) ||
                 string.IsNullOrEmpty(adPerson.DisplayName)))
            {
                return(null);
            }

            var(firstName, lastName) = GetAdPersonFirstAndLastName(adPerson);

            var person = await _personRepository.FindByMobileNumberAndNameAsync(
                adPerson.MobileNumber,
                firstName,
                lastName);

            if (person != null)
            {
                person.Oid           = adPerson.Oid;
                person.UpdatedAt     = DateTime.Now;
                person.UpdatedById   = _personCreatedByCache.Id;
                person.UpdatedByUser = _personCreatedByCache.Username;
            }

            return(person);
        }
        public void GetAdPersonNameFromDisplayNameWithNoSpaces()
        {
            // Arrange
            const string testDisplayName = "Firstname.Middle.Lastname";

            var adPerson = new AdPerson("oid", "username", "email")
            {
                DisplayName = testDisplayName
            };

            // Act
            var(firstName, lastName) = _service.GetAdPersonFirstAndLastName(adPerson);

            // Assert
            Assert.AreEqual(testDisplayName, firstName);
            Assert.AreEqual("#", lastName);
        }
        public void GetAdPersonNameFromDisplayName()
        {
            // Arrange
            const string testDisplayName = "Jul E Nissen";

            var adPerson = new AdPerson("oid", "username", "email")
            {
                DisplayName = testDisplayName
            };

            // Act
            var(firstName, lastName) = _service.GetAdPersonFirstAndLastName(adPerson);

            // Assert
            Assert.AreEqual("Jul E", firstName);
            Assert.AreEqual("Nissen", lastName);
        }
        public async Task <AdPerson?> GetAdPersonByOidAsync(string userOid)
        {
            var graphClient = await CreateClient();

            _log.LogInformation($"Querying microsoft graph for user with oid {userOid}");

            try
            {
                var user = await graphClient.Users[userOid].Request().GetAsync();

                // Username is e-mail by default.
                // Check that it does not contain the internal name (result of bad data in AD), use UPN in that case.
                var userNameFromGraph = !string.IsNullOrEmpty(user.Mail) && user.Mail.Contains(EquinorInternalAzureName)
                    ? user.UserPrincipalName
                    : user.Mail;

                var userName = userNameFromGraph;
                var email    = userNameFromGraph;

                if (string.IsNullOrEmpty(userName))
                {
                    // set UserPrincipalName if e-mail is undefined
                    userName = user.UserPrincipalName;

                    if (user.UserPrincipalName.Contains("@"))
                    {
                        email = user.UserPrincipalName;
                    }
                }

                var adPerson = new AdPerson(user.Id, userName, email)
                {
                    GivenName    = user.GivenName,
                    Surname      = user.Surname,
                    MobileNumber = user.MobilePhone,
                    DisplayName  = user.DisplayName
                };

                return(adPerson);
            }
            catch (ServiceException)
            {
                _log.LogInformation($"User with oid {userOid} not found in user directory");
                return(null);
            }
        }
        public void GetAdPersonNameFromGivenAndSurname()
        {
            // Arrange
            const string testGivenName = "TestGivenName";
            const string testSurname   = "TestSurname";

            var adPerson = new AdPerson("oid", "username", "email")
            {
                GivenName = testGivenName,
                Surname   = testSurname
            };

            // Act
            var(firstName, lastName) = _service.GetAdPersonFirstAndLastName(adPerson);

            // Assert
            Assert.AreEqual(testGivenName, firstName);
            Assert.AreEqual(testSurname, lastName);
        }
        public (string firstName, string lastName) GetAdPersonFirstAndLastName(AdPerson adPerson)
        {
            if (!string.IsNullOrEmpty(adPerson.GivenName) && !string.IsNullOrEmpty(adPerson.Surname))
            {
                return(adPerson.GivenName, adPerson.Surname);
            }

            if (!string.IsNullOrEmpty(adPerson.DisplayName) && adPerson.DisplayName.Contains(" "))
            {
                if (adPerson.DisplayName.Contains(","))
                {
                    // A naming error in Azure sometimes cause DisplayName to be formatted "Lastname, Firstname".
                    // The following is a best effort fix to put Lastname last and remove the comma.
                    var nameToFix       = adPerson.DisplayName;
                    var assumedLastName = nameToFix.Substring(0, nameToFix.IndexOf(",", StringComparison.InvariantCulture));
                    nameToFix = nameToFix.Remove(0, assumedLastName.Length);
                    nameToFix = nameToFix.Replace(",", string.Empty);
                    nameToFix = nameToFix.TrimStart();

                    adPerson.DisplayName = $"{nameToFix} {assumedLastName}";
                }

                // last name will be set to the last part of the name, regardless of any middle-name variants (best effort)
                var indexOfLastSpace = adPerson.DisplayName.LastIndexOf(" ", StringComparison.InvariantCulture);
                var firstName        = adPerson.DisplayName.Substring(0, indexOfLastSpace);
                var lastName         = adPerson.DisplayName.Substring(indexOfLastSpace + 1);

                return(firstName, lastName);
            }

            if (!string.IsNullOrEmpty(adPerson.DisplayName))
            {
                // As a last resort we are putting the entire DisplayName into the firstname field, and the "#" character as lastname.
                return(adPerson.DisplayName, "#");
            }

            throw new Exception($"Could not determine first or last name for user with Oid: {adPerson.Oid}");
        }
        private async Task <List <Person> > GetReconcilePersons(AdPerson adPerson)
        {
            var(firstName, lastName) = GetAdPersonFirstAndLastName(adPerson);

            var possibleMatches = await _personRepository.FindPossibleMatches(
                adPerson.MobileNumber,
                firstName,
                lastName,
                adPerson.Username,
                adPerson.Email);

            var reconcilePersons    = new List <Person>();
            var adPersonEmailDomain = GetEmailAddressDomain(adPerson.Username);

            // In order to set reconcile, the existing and new users e-mail domains must match.
            // Otherwise, it is most likely a new affiliate user and should not be reconciled.
            foreach (var person in possibleMatches.ToList())
            {
                var reconcilePersonEmailDomains = new List <string>
                {
                    GetEmailAddressDomain(person.Email)
                };

                if (person.UserName.Contains("@"))
                {
                    // Also add domain from username as a candidate if it's an e-mail address.
                    reconcilePersonEmailDomains.Add(GetEmailAddressDomain(person.UserName));
                }

                if (reconcilePersonEmailDomains.Contains(adPersonEmailDomain))
                {
                    reconcilePersons.Add(person);
                }
            }

            return(reconcilePersons);
        }