Exemple #1
0
            /// <summary>
            /// Executes the workflow to do user authentication.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override GetEmployeePermissionsResponse Process(GetEmployeePermissionsRequest request)
            {
                ThrowIf.Null(request, "request");
                GetEmployeesServiceRequest  getEmployeeRequest = new GetEmployeesServiceRequest(request.StaffId, QueryResultSettings.SingleRecord);
                GetEmployeesServiceResponse employeeResponse   = this.Context.Execute <GetEmployeesServiceResponse>(getEmployeeRequest);
                Employee employee = employeeResponse.Employees.SingleOrDefault();

                if (employee == null)
                {
                    return(new GetEmployeePermissionsResponse(employee));
                }

                // Check if the requested Employee object is same as logged-on user.
                // If not, check staff have manager permission.
                // If the staff is not manager, do not return permissions
                if (!string.Equals(request.StaffId, this.Context.GetPrincipal().UserId))
                {
                    try
                    {
                        this.Context.Execute <Response>(new CheckAccessIsManagerServiceRequest());
                    }
                    catch (UserAuthorizationException)
                    {
                        return(new GetEmployeePermissionsResponse(employee));
                    }
                }

                GetEmployeePermissionsDataRequest permissionsDataRequest = new GetEmployeePermissionsDataRequest(request.StaffId, new ColumnSet());

                employee.Permissions = this.Context.Execute <SingleEntityDataServiceResponse <EmployeePermissions> >(permissionsDataRequest).Entity;
                return(new GetEmployeePermissionsResponse(employee));
            }
Exemple #2
0
            /// <summary>
            /// Gets the employee permission details.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="staffId">The staff identifier.</param>
            /// <returns>
            /// The employee permission request.
            /// </returns>
            /// <exception cref="UserAuthenticationException">When the employee does not exists.</exception>
            public static EmployeePermissions GetEmployeePermissions(RequestContext context, string staffId)
            {
                ThrowIf.Null(context, "context");
                GetEmployeeDataRequest dataRequest = new GetEmployeeDataRequest(staffId, QueryResultSettings.SingleRecord);
                Employee employee = context.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);
                }

                // Check if the requested Employee object is same as logged-on user.
                // If not, check staff have manager permission.
                if (!string.Equals(staffId, context.GetPrincipal().UserId))
                {
                    var checkAccessRequest = new CheckAccessIsManagerServiceRequest();
                    context.Execute <NullResponse>(checkAccessRequest);
                }

                GetEmployeePermissionsDataRequest permissionsDataRequest = new GetEmployeePermissionsDataRequest(staffId, new ColumnSet());

                employee.Permissions = context.Execute <SingleEntityDataServiceResponse <EmployeePermissions> >(permissionsDataRequest).Entity;

                return(employee.Permissions);
            }
 private static void OnGetEmployeePermissionsExecuting(GetEmployeePermissionsDataRequest request)
 {
     // Check if the requested Employee object is same as logged-on user.
     // If not, check staff have manager permission.
     if (!string.Equals(request.StaffId, request.RequestContext.GetPrincipal().UserId))
     {
         var checkAccessRequest = new CheckAccessIsManagerServiceRequest();
         request.RequestContext.Execute <Response>(checkAccessRequest);
     }
 }
            /// <summary>
            /// Verifies whether local authorization must be used as a fallback mode in case of error on remote authorization.
            /// </summary>
            /// <returns>A value indicating whether local authorization must be used as a fallback mode in case of error on remote authorization.</returns>
            private bool MustFallbackToLocalAuthorization()
            {
                // we can only fallback to local database if we have a channel id provided
                if (this.CanUseLocalDatabase)
                {
                    EmployeePermissions employeePermissions = null;

                    try
                    {
                        // Create a temporary context with the employee for getting employee permissions.
                        RequestContext tempContext = new RequestContext(this.context.Runtime);
                        var            employee    = new Employee()
                        {
                            StaffId = this.StaffId
                        };
                        ICommercePrincipal principal = this.context.GetPrincipal();
                        CommerceIdentity   identity  = new CommerceIdentity(
                            employee,
                            new Device()
                        {
                            DeviceNumber     = principal.DeviceNumber,
                            Token            = principal.DeviceToken,
                            ChannelId        = principal.ChannelId,
                            TerminalRecordId = principal.TerminalId
                        });
                        tempContext.SetPrincipal(new CommercePrincipal(identity));

                        // Get employee permissions
                        GetEmployeePermissionsDataRequest getEmployeePermissionsDataRequest = new GetEmployeePermissionsDataRequest(
                            this.StaffId,
                            new ColumnSet());

                        employeePermissions = tempContext.Execute <SingleEntityDataServiceResponse <EmployeePermissions> >(
                            getEmployeePermissionsDataRequest).Entity;
                    }
                    catch (Exception exception)
                    {
                        // this method occurs in an error handling scenario
                        // if this fails, we do not want to break the flow
                        // so we just log the exception internally
                        RetailLogger.Instance.CrtServicesStaffAuthorizationServiceGetEmployeePermissionsFailure(exception);
                    }

                    // we can fallback if it is allowed by user permissions
                    return(employeePermissions != null && employeePermissions.ContinueOnTSErrors);
                }

                return(false);
            }
            /// <summary>
            /// Gets the current shift and sets it on the context.
            /// </summary>
            /// <param name="context">The context.</param>
            private static void PopulateContextWithShiftInformation(RequestContext context)
            {
                ShiftDataQueryCriteria criteria = new ShiftDataQueryCriteria
                {
                    ChannelId  = context.GetPrincipal().ChannelId,
                    TerminalId = context.GetTerminal().TerminalId,
                    StaffId    =
                        string.IsNullOrWhiteSpace(context.GetPrincipal().OriginalUserId)
                            ? context.GetPrincipal().UserId
                            : context.GetPrincipal().OriginalUserId,
                    Status                    = (int)ShiftStatus.Open,
                    SearchByStaffId           = true,
                    SearchByCurrentStaffId    = true,
                    SearchByTerminalId        = true,
                    SearchByCurrentTerminalId = true
                };

                // Get original staff id (not a manager's) during elevated permission operations.
                GetEmployeePermissionsDataRequest permissionsDataRequest = new GetEmployeePermissionsDataRequest(criteria.StaffId, new ColumnSet());
                EmployeePermissions employeePermissions = context.Runtime.Execute <SingleEntityDataServiceResponse <EmployeePermissions> >(
                    permissionsDataRequest, context, skipRequestTriggers: true).Entity;

                if (employeePermissions != null)
                {
                    criteria.IncludeSharedShifts = employeePermissions.HasManagerPrivileges ||
                                                   employeePermissions.AllowManageSharedShift ||
                                                   employeePermissions.AllowUseSharedShift ||
                                                   employeePermissions.AllowMultipleShiftLogOn;
                }

                GetShiftDataDataRequest dataServiceRequest = new GetShiftDataDataRequest(criteria, QueryResultSettings.SingleRecord);
                Shift shift = context.Runtime.Execute <EntityDataServiceResponse <Shift> >(dataServiceRequest, context, skipRequestTriggers: true).PagedEntityCollection.Results.FirstOrDefault();

                if (shift != null)
                {
                    // TerminalId is the identifier of the terminal which creates the shift
                    context.GetPrincipal().ShiftId         = shift.ShiftId;
                    context.GetPrincipal().ShiftTerminalId = shift.TerminalId;
                }
            }
            /// <summary>
            /// Gets the employee permission group for the staff identifier.
            /// </summary>
            /// <param name="request">The data service request.</param>
            /// <returns>The data service response.</returns>
            private SingleEntityDataServiceResponse <EmployeePermissions> GetEmployeePermissions(GetEmployeePermissionsDataRequest request)
            {
                EmployeeDataManager dataManager = this.GetDataManagerInstance(request.RequestContext);
                var result = dataManager.GetEmployeePermissions(request.StaffId, request.ColumnSet);

                return(new SingleEntityDataServiceResponse <EmployeePermissions>(result));
            }
            /// <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);
            }
Exemple #8
0
            /// <summary>
            /// Authenticates and authorizes the user.
            /// </summary>
            /// <param name="request">UserAuthenticationRequest request object.</param>
            /// <param name="device">The device.</param>
            /// <returns>Employee object.</returns>
            internal static Employee AuthenticateAndAuthorizeUser(UserAuthenticationRequest request, Device device)
            {
                // Authenticate
                Employee       employee;
                string         staffId; // in extended authentication cases, this value might be empty
                RequestContext executionContext = request.RequestContext;

                if (device != null && request.RequestContext.GetPrincipal().IsChannelAgnostic)
                {
                    executionContext = CreateRequestContext(string.Empty, device, request.RequestContext.Runtime);
                }

                UserLogOnServiceRequest authenticateServiceRequest = new UserLogOnServiceRequest(
                    request.StaffId,
                    request.Password,
                    request.Credential,
                    request.GrantType,
                    request.AdditionalAuthenticationData);
                UserLogOnServiceResponse response = executionContext.Execute <UserLogOnServiceResponse>(authenticateServiceRequest);

                staffId = response.StaffId;

                // Authorize only if this was for elevate user
                if (request.RetailOperation != RetailOperation.None)
                {
                    var authorizeStaffRequest = new StaffAuthorizationServiceRequest(
                        request.RequestContext.GetPrincipal().UserId,
                        request.RetailOperation);

                    bool currentUserAlreadyAuthorizedForOperation = true;

                    try
                    {
                        executionContext.Execute <StaffAuthorizationServiceResponse>(authorizeStaffRequest);
                    }
                    catch (UserAuthorizationException authorizationException)
                    {
                        if (string.Equals(authorizationException.ErrorResourceId, SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthorizationFailed.ToString(), StringComparison.Ordinal))
                        {
                            currentUserAlreadyAuthorizedForOperation = false;
                        }
                    }

                    if (currentUserAlreadyAuthorizedForOperation)
                    {
                        RetailLogger.Log.CrtWorkflowUserAuthenticationUserAlreadyHasAccessToTheTargetedOperation(request.RequestContext.GetPrincipal().UserId, request.RetailOperation.ToString());
                        throw new UserAuthorizationException(SecurityErrors.Microsoft_Dynamics_Commerce_Runtime_AuthorizationFailed, "The current user is already authorized to perform the targeted operation.");
                    }

                    authorizeStaffRequest = new StaffAuthorizationServiceRequest(
                        staffId,
                        request.RetailOperation);

                    StaffAuthorizationServiceResponse authorizationResponse =
                        executionContext.Execute <StaffAuthorizationServiceResponse>(authorizeStaffRequest);
                    employee = authorizationResponse.Employee;
                }
                else
                {
                    // The employee has already been authenticated above.
                    employee = new Employee()
                    {
                        StaffId = staffId
                    };

                    // if we created a new context, then we need to update it here with the staff id
                    executionContext = CreateRequestContext(staffId, device, request.RequestContext.Runtime);

                    // This is needed for offline scenarios/logon because as opposed to online/RS in CRT there is no user look up when the identity is presented on API calls.
                    GetEmployeePermissionsDataRequest permissionsDataRequest = new GetEmployeePermissionsDataRequest(staffId, new ColumnSet());
                    employee.Permissions = executionContext.Execute <SingleEntityDataServiceResponse <EmployeePermissions> >(permissionsDataRequest).Entity;
                }

                LogAuthenticationRequest(executionContext, employee.StaffId, AuthenticationStatus.Success, AuthenticationOperation.CreateToken);

                return(employee);
            }