/// <summary>
            /// Authenticate the user.
            /// </summary>
            /// <param name="request">The device activation request.</param>
            /// <returns>The device activation response.</returns>
            private static StaffLogOnRealtimeResponse LogOnUser(StaffLogOnRealtimeRequest request)
            {
                // Mimic the real time TS logon
                Employee employee = EmployeeLogOnStore(request, request.RequestContext.GetChannelConfiguration());

                return(new StaffLogOnRealtimeResponse(employee));
            }
            /// <summary>
            /// Authenticate the user.
            /// </summary>
            /// <param name="request">The device activation request.</param>
            /// <returns>The device activation response.</returns>
            private static StaffLogOnRealtimeResponse LogOnUser(StaffLogOnRealtimeRequest request)
            {
                Employee employee;

                var transactionService = new TransactionService.TransactionServiceClient(request.RequestContext);

                long channelId                = request.ChannelId.GetValueOrDefault(0);
                long terminalId               = request.TerminalRecordId.GetValueOrDefault(0);
                bool logOntoStore             = request.ChannelId.HasValue;
                bool skipPasswordVerification = string.IsNullOrWhiteSpace(request.StaffPassword);

                employee = transactionService.RetailServerStaffLogOn(request.StaffId, channelId, terminalId, request.StaffPassword, logOntoStore, skipPasswordVerification);
                return(new StaffLogOnRealtimeResponse(employee));
            }
            /// <summary>
            /// Logon the user on local store.
            /// </summary>
            /// <param name="request">The device activation request.</param>
            /// <param name="channelConfiguration">The channel configuration.</param>
            /// <returns>The device activation response.</returns>
            private static Employee EmployeeLogOnStore(StaffLogOnRealtimeRequest request, ChannelConfiguration channelConfiguration)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                Employee employee = null, localEmployee = null;
                string   passwordHash = string.Empty;
                string   staffId      = request.StaffId;

                if (request.ChannelId.HasValue && request.RequestContext.GetPrincipal().ChannelId == request.ChannelId.Value)
                {
                    // Get employee salt and password hash algorithm.
                    GetEmployeePasswordCryptoInfoDataRequest employeePasswordCryptoInfoRequest = new GetEmployeePasswordCryptoInfoDataRequest(request.ChannelId.GetValueOrDefault(), staffId);
                    var    passwordCryptoInfo = request.RequestContext.Execute <SingleEntityDataServiceResponse <EmployeePasswordCryptoInfo> >(employeePasswordCryptoInfoRequest).Entity;
                    string salt = passwordCryptoInfo.PasswordSalt ?? string.Empty;
                    string passwordHashAlgorithm = passwordCryptoInfo.PasswordHashAlgorithm;

                    if (!string.IsNullOrEmpty(request.StaffPassword))
                    {
                        if (string.IsNullOrEmpty(passwordHashAlgorithm))
                        {
                            // get hash algorithm from the transaction service profile.
                            var getTransactionServiceProfileDataRequest         = new GetTransactionServiceProfileDataRequest();
                            TransactionServiceProfile transactionServiceProfile = request.RequestContext.Execute <SingleEntityDataServiceResponse <TransactionServiceProfile> >(
                                getTransactionServiceProfileDataRequest).Entity;
                            passwordHashAlgorithm = transactionServiceProfile.StaffPasswordHash;
                        }

                        HashDataServiceRequest hashDataServiceRequest = new HashDataServiceRequest(request.StaffPassword, passwordHashAlgorithm, staffId, salt);
                        passwordHash = request.RequestContext.Execute <HashDataServiceResponse>(hashDataServiceRequest).Data;
                    }

                    // Logon to the store
                    EmployeeLogOnStoreDataRequest dataRequest = new EmployeeLogOnStoreDataRequest(
                        request.ChannelId.Value,
                        staffId,
                        passwordHash,
                        new ColumnSet());

                    localEmployee = request.RequestContext.Execute <SingleEntityDataServiceResponse <Employee> >(dataRequest).Entity;
                    if (localEmployee == null || localEmployee.Permissions == null)
                    {
                        throw new UserAuthenticationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthenticationFailed);
                    }

                    // Return new object for the logged-on employee with only the required fields to ensure not returning any sensitive data.
                    employee               = new Employee();
                    employee.Permissions   = new EmployeePermissions();
                    employee.Name          = localEmployee.Name;
                    employee.StaffId       = localEmployee.StaffId;
                    employee.NameOnReceipt = localEmployee.NameOnReceipt;
                    employee.Permissions   = localEmployee.Permissions;
                    employee.Images        = localEmployee.Images;
                    employee.PasswordLastChangedDateTime = localEmployee.PasswordLastChangedDateTime;

                    // Lock the user if multiple logins are not allowed.
                    if (!employee.Permissions.AllowMultipleLogins && channelConfiguration != null)
                    {
                        LockUserAtLogOnDataRequest lockDataRequest = new LockUserAtLogOnDataRequest(
                            request.ChannelId.Value,
                            request.RequestContext.GetTerminal().TerminalId,
                            staffId,
                            channelConfiguration.InventLocationDataAreaId);
                        bool locked = request.RequestContext.Execute <SingleEntityDataServiceResponse <bool> >(lockDataRequest).Entity;
                        if (!locked)
                        {
                            throw new UserAuthenticationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_UserLogonAnotherTerminal);
                        }
                    }
                }
                else
                {
                    RequestContext getEmployeeRequestContext;

                    // Employee record is associated with channel
                    // Decide what context is to be used to retrieve it
                    if (request.RequestContext.GetPrincipal().IsChannelAgnostic)
                    {
                        // If the context is channel agnostic (no channel information), then use current (first published channel in this case)
                        GetChannelIdServiceRequest  getChannelRequest  = new GetChannelIdServiceRequest();
                        GetChannelIdServiceResponse getChannelResponse = request.RequestContext.Execute <GetChannelIdServiceResponse>(getChannelRequest);

                        var anonymousIdentity = new CommerceIdentity()
                        {
                            ChannelId = getChannelResponse.ChannelId
                        };

                        getEmployeeRequestContext = new RequestContext(request.RequestContext.Runtime);
                        getEmployeeRequestContext.SetPrincipal(new CommercePrincipal(anonymousIdentity));
                    }
                    else
                    {
                        // If the request has channel information, then use current context
                        getEmployeeRequestContext = request.RequestContext;
                    }

                    GetEmployeeDataRequest dataRequest = new GetEmployeeDataRequest(staffId, QueryResultSettings.SingleRecord);
                    employee = getEmployeeRequestContext.Execute <SingleEntityDataServiceResponse <Employee> >(dataRequest).Entity;
                    if (employee == null)
                    {
                        string message = string.Format(CultureInfo.InvariantCulture, "The specified employee ({0}) was not found.", staffId);
                        throw new UserAuthenticationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthenticationFailed, message);
                    }

                    ICommercePrincipal principal = request.RequestContext.GetPrincipal();
                    RequestContext     contextForOperationAuthorization;
                    if (principal.IsAnonymous)
                    {
                        // create an authenticated context that can be used for specific operation authorization
                        RequestContext   authenticatedContext = new RequestContext(request.RequestContext.Runtime);
                        CommerceIdentity identity             = new CommerceIdentity(
                            employee,
                            new Device()
                        {
                            DeviceNumber     = principal.DeviceNumber,
                            Token            = principal.DeviceToken,
                            ChannelId        = principal.ChannelId,
                            TerminalRecordId = principal.TerminalId
                        });
                        authenticatedContext.SetPrincipal(new CommercePrincipal(identity));

                        contextForOperationAuthorization = authenticatedContext;
                    }
                    else
                    {
                        contextForOperationAuthorization = request.RequestContext;
                    }

                    GetEmployeePermissionsDataRequest permissionsDataRequest = new GetEmployeePermissionsDataRequest(staffId, new ColumnSet());
                    employee.Permissions = contextForOperationAuthorization.Execute <SingleEntityDataServiceResponse <EmployeePermissions> >(permissionsDataRequest).Entity;
                }

                return(employee);
            }
            /// <summary>
            /// Authorizes the staff using the real time service.
            /// </summary>
            /// <param name="verifyEmployeeLocalDatabaseFallbackDelegate">A delegate that performs the verification checks against the local database that is used in case local fallback is required.</param>
            /// <returns>The verified employee.</returns>
            /// <exception cref="InvalidOperationException">Due to invalid verification type.</exception>
            /// <exception cref="HeadquarterTransactionServiceException">When the real time service process the request but returns an error.</exception>
            /// <exception cref="CommunicationException">When it was not possible to communicate with the real time service.</exception>
            public Employee VerifyEmployeeRealTimeService(Func <Employee> verifyEmployeeLocalDatabaseFallbackDelegate)
            {
                ThrowIf.Null(verifyEmployeeLocalDatabaseFallbackDelegate, "verifyEmployeeLocalDatabaseFallbackDelegate");

                Employee employee = null;

                if (this.verificationType == SecurityVerificationType.Authentication)
                {
                    throw new InvalidOperationException("Real time service does not support authentication only. Please use Authentication and Authorization for real time calls.");
                }

                // if we don't have a stored result, or the stored result is for a call with different paramters, then call TS
                if (this.realTimeStaffVerificationEmployee == null)
                {
                    // retail operation, logon key, extra data and all values from device object but channelid and terminal are not used by transaction service
                    StaffLogOnRealtimeRequest request = new StaffLogOnRealtimeRequest(
                        this.ChannelId,
                        this.TerminalRecordId,
                        this.StaffId,
                        this.password);

                    bool mustFallbackToLocal = false;

                    try
                    {
                        employee = this.realTimeStaffVerificationEmployee = this.context.Execute <StaffLogOnRealtimeResponse>(request).Employee;
                    }
                    catch (UserAuthenticationException)
                    {
                        throw;
                    }
                    catch (UserAuthorizationException)
                    {
                        throw;
                    }
                    catch (Exception exception)
                    {
                        // if we could not make the real time call, check if we can validate user locally
                        if (this.MustFallbackToLocalAuthorization())
                        {
                            RetailLogger.Instance.CrtServicesStaffAuthorizationServiceRetalTimeServiceFailure(exception);

                            // and fallback to local verification
                            mustFallbackToLocal = true;
                        }
                        else
                        {
                            RetailLogger.Log.CrtServicesStaffAuthorizationServiceUserNotAuthorizedToFallbackToLocalDatabase(this.StaffId, exception);

                            // because user does not have permission to perform a local authentication/authorization flow
                            // we need to fail authorization
                            throw new UserAuthorizationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthorizationFailed, exception, "User not authorized");
                        }
                    }

                    // if we fallback, do not cache result since it does not come from transaction service
                    if (mustFallbackToLocal)
                    {
                        employee = verifyEmployeeLocalDatabaseFallbackDelegate();
                    }
                }
                else
                {
                    employee = this.realTimeStaffVerificationEmployee;
                }

                return(employee);
            }