/// <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 the workflow to get the currently logged in employee. /// </summary> /// <param name="request">The request.</param> /// <returns>The response.</returns> protected override GetCurrentEmployeeResponse Process(GetCurrentEmployeeRequest request) { ThrowIf.Null(request, "request"); // authorize employee will return employee and all permissions var staffRequest = new StaffAuthorizationServiceRequest( request.RequestContext.Runtime.CurrentPrincipal.UserId, RetailOperation.None); var response = this.Context.Execute <StaffAuthorizationServiceResponse>(staffRequest); // get the full employee object from the database. Employee employee = response.Employee; if (employee != null && !string.IsNullOrWhiteSpace(employee.StaffId)) { var employeePermission = employee.Permissions; QueryResultSettings settings = new QueryResultSettings(new PagingInfo(top: 1)); GetEmployeesServiceRequest employeeRequest = new GetEmployeesServiceRequest(employee.StaffId, settings); var employeeResponse = this.Context.Execute <GetEmployeesServiceResponse>(employeeRequest); if (employeeResponse != null) { employee = employeeResponse.Employees.SingleOrDefault(); // Set the employee permission as persisted during staff authorization call. employee.Permissions = employeePermission; } // Set the number of days to password expiry on the employee. int passwordExpiryIntervalInDays = 0; int passwordExpiryNotificationThreshold = 0; ChannelConfiguration channelConfiguration = this.Context.GetChannelConfiguration(); if (channelConfiguration != null) { passwordExpiryIntervalInDays = channelConfiguration.PasswordExpiryIntervalInDays; passwordExpiryNotificationThreshold = channelConfiguration.PasswordExpiryNotificationThresholdInDays; } employee.NumberOfDaysToPasswordExpiry = CalculateNumberOfDaysToPasswordExpiry(passwordExpiryIntervalInDays, passwordExpiryNotificationThreshold, employee.PasswordLastChangedDateTime); } return(new GetCurrentEmployeeResponse(employee)); }
/// <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); }