/// <summary>
 /// Throws if invalid logon configuration.
 /// </summary>
 /// <param name="logOnConfiguration">The log on configuration.</param>
 /// <exception cref="System.InvalidOperationException">Invalid LogOnConfiguration of a request.</exception>
 private static void ThrowIfInvalidLogonConfiguration(LogOnConfiguration logOnConfiguration)
 {
     if (logOnConfiguration != LogOnConfiguration.RealTimeService)
     {
         throw new InvalidOperationException("Invalid LogOnConfiguration of a request.");
     }
 }
            /// <summary>
            /// Authorizes the retail staff.
            /// </summary>
            /// <param name="staffAuthorizationRequest">The retail staff authorization request.</param>
            /// <returns>The service response.</returns>
            private static Response AuthorizeStaff(StaffAuthorizationServiceRequest staffAuthorizationRequest)
            {
                RequestContext     context       = staffAuthorizationRequest.RequestContext;
                ICommercePrincipal principal     = context.GetPrincipal();
                string             staffId       = string.IsNullOrWhiteSpace(staffAuthorizationRequest.StaffId) ? principal.UserId : staffAuthorizationRequest.StaffId;
                long?           channelId        = principal.IsChannelAgnostic ? null : (long?)principal.ChannelId;
                long?           terminalRecordId = principal.IsTerminalAgnostic ? null : (long?)principal.TerminalId;
                RetailOperation operation        = staffAuthorizationRequest.RetailOperation;
                Employee        employee;

                if (string.IsNullOrWhiteSpace(staffId))
                {
                    throw new UserAuthorizationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthorizationFailed, "UserId is missing from principal.");
                }

                if (channelId.HasValue && !principal.IsTerminalAgnostic && !terminalRecordId.HasValue)
                {
                    throw new UserAuthorizationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthorizationFailed, "When channel identififer is provided on principal, terminal record identfier must also be.");
                }

                StaffRealTimeSecurityValidationHelper staffSecurityHelper = StaffRealTimeSecurityValidationHelper.Create(
                    context,
                    SecurityVerificationType.Authorization,
                    staffId,
                    channelId,
                    terminalRecordId,
                    password: string.Empty);

                // we can only check values against database if the principal is bound to a channel
                if (!principal.IsChannelAgnostic)
                {
                    VerifyThatOrgUnitIsPublished(context, channelId.Value);
                }

                // for authorization, we always want to go to local DB, we only go to headquarters if we don't have a channel
                // to access the DB
                LogOnConfiguration logOnConfiguration = principal.IsChannelAgnostic
                    ? LogOnConfiguration.RealTimeService
                    : LogOnConfiguration.LocalDatabase;

                // authorize employee based on configuration
                employee = ExecuteWorkWithLocalFallback(
                    logOnConfiguration,
                    staffSecurityHelper,
                    () =>
                {
                    return(AuthorizeEmployeeLocalDatabase(context, staffSecurityHelper.StaffId, staffAuthorizationRequest.EnforceSessionToBeOpened));
                });

                // Validates whether the staff can perform the requested operation
                ValidateEmployeePermissionForOperation(context, operation, employee);

                return(new StaffAuthorizationServiceResponse(employee));
            }
            /// <summary>
            /// Executes work based against RTS or Local Database based on <paramref name="authorizationConfiguration"/>.
            /// If <paramref name="authorizationConfiguration"/> is <see cref="LogOnConfiguration.RealTimeService"/> then
            /// business logic is performed using RealTime service. If RealTime is not available and <see cref="Employee"/> is allowed to fallback to local database checks, then
            /// the delegate <paramref name="localDatabaseAction"/> will be executed. In case <paramref name="authorizationConfiguration"/> is <see cref="LogOnConfiguration.LocalDatabase"/>,
            /// then authorization is performed solely using local database.
            /// </summary>
            /// <param name="authorizationConfiguration">Indicates whether the logic should run, against local database or against the real time service.</param>
            /// <param name="staffSecurityHelper">The staff security validation helper instance.</param>
            /// <param name="localDatabaseAction">A delegate for the local execution of the authorization logic.</param>
            /// <returns>The employee.</returns>
            private static Employee ExecuteWorkWithLocalFallback(LogOnConfiguration authorizationConfiguration, StaffRealTimeSecurityValidationHelper staffSecurityHelper, Func <Employee> localDatabaseAction)
            {
                Employee employee = null;
                string   errorMessage;

                switch (authorizationConfiguration)
                {
                case LogOnConfiguration.RealTimeService:
                    try
                    {
                        employee = staffSecurityHelper.VerifyEmployeeRealTimeService(() =>
                        {
                            return(localDatabaseAction());
                        });
                    }
                    catch (HeadquarterTransactionServiceException exception)
                    {
                        throw new UserAuthorizationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterTransactionServiceMethodCallFailure, exception, exception.Message)
                              {
                                  LocalizedMessage = exception.LocalizedMessage
                              };
                    }
                    catch (CommerceException exception)
                    {
                        // The error code to be persisted
                        throw new UserAuthorizationException(
                                  SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterCommunicationFailure,
                                  exception,
                                  exception.Message);
                    }
                    catch (Exception exception)
                    {
                        // any exceptions that might happen will cause the authorization to fail
                        throw new UserAuthorizationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthorizationFailed,
                                                             exception,
                                                             exception.Message);
                    }

                    break;

                case LogOnConfiguration.LocalDatabase:
                    employee = localDatabaseAction();
                    break;

                default:
                    errorMessage = string.Format(
                        CultureInfo.InvariantCulture,
                        "The authorization configuration value '{0}' is not supported.",
                        authorizationConfiguration);
                    throw new NotSupportedException(errorMessage);
                }

                return(employee);
            }