private static RequestContext CreateRequestContext(string staffId, Device device, ICommerceRuntime runtime) { CommerceIdentity identity; if (string.IsNullOrWhiteSpace(staffId)) { identity = new CommerceIdentity(device); identity.Roles.Add(CommerceRoles.Anonymous); } else { var employee = new Employee() { StaffId = staffId }; identity = new CommerceIdentity(employee, device); } CommercePrincipal principal = new CommercePrincipal(identity); RequestContext context = new RequestContext(runtime); context.SetPrincipal(principal); return(context); }
/// <summary> /// Retrieves the transaction service profile. /// </summary> /// <param name="context">The request context.</param> /// <returns>The transaction service profile.</returns> private static TransactionServiceProfile GetTransactionServiceProfile(Microsoft.Dynamics.Commerce.Runtime.RequestContext context) { GetTransactionServiceProfileDataRequest request = new GetTransactionServiceProfileDataRequest(); Microsoft.Dynamics.Commerce.Runtime.RequestContext getTransactionServiceProfileRequestContext; // Transaction service profile is associated with channel // Decide what context is to be used to retrieve it if (context.GetPrincipal().IsChannelAgnostic) { // If the context is channel agnostic (no channel information), then use any channel available to retrieve the profile GetChannelIdServiceRequest getChannelRequest = new GetChannelIdServiceRequest(); GetChannelIdServiceResponse getChannelResponse = context.Execute <GetChannelIdServiceResponse>(getChannelRequest); long anyChannelId = getChannelResponse.ChannelId; var anonymousIdentity = new CommerceIdentity() { ChannelId = anyChannelId }; getTransactionServiceProfileRequestContext = new Microsoft.Dynamics.Commerce.Runtime.RequestContext(context.Runtime); getTransactionServiceProfileRequestContext.SetPrincipal(new CommercePrincipal(anonymousIdentity)); } else { // If the request has channel information, then use current context to retrieve the transaction service profile getTransactionServiceProfileRequestContext = context; } return(getTransactionServiceProfileRequestContext.Execute <SingleEntityDataServiceResponse <TransactionServiceProfile> >(request).Entity); }
/// <summary> /// Default constructor /// </summary> public DAXVersionRealTimeServiceController() { // Need to set the ChannelId explicitly this.CommerceIdentity = User.Identity as CommerceIdentity; this.CommerceIdentity.ChannelId = 5637144662; TransactionServiceClient = new TransactionServiceClient(CommerceRuntimeManager.Runtime.CreateRequestContext(new RealtimeRequest())); }
/// <summary> /// Gets the employee identity given the external identity. /// </summary> /// <param name="request">The device activation request.</param> /// <returns>The device activation response.</returns> private static Response GetEmployeeIdentityByExternalIdentity(GetEmployeeIdentityByExternalIdentityRealtimeRequest request) { TransactionService.TransactionServiceClient transactionService = new TransactionService.TransactionServiceClient(request.RequestContext); Employee employee = transactionService.GetRetailServerStaffByExternalIdentity(request.ExternalIdentityId, request.ExternalIdentitySubId); var employeeIdentity = new CommerceIdentity(employee, null); return(new GetEmployeeIdentityByExternalIdentityRealtimeResponse(employeeIdentity)); }
/// <summary> /// Executes the workflow to do user authentication. /// </summary> /// <param name="request">The request.</param> /// <returns>The response.</returns> protected override UserAuthenticationResponse Process(UserAuthenticationRequest request) { ThrowIf.Null(request, "request"); Device device = null; CommerceIdentity identity; Employee employee; string deviceId = string.IsNullOrWhiteSpace(request.DeviceId) ? request.RequestContext.GetPrincipal().DeviceNumber : request.DeviceId; string deviceToken = string.IsNullOrWhiteSpace(request.DeviceToken) ? request.RequestContext.GetPrincipal().DeviceToken : request.DeviceToken; try { // Authenticate device only when the device token is specified if (!string.IsNullOrWhiteSpace(deviceToken)) { device = AuthenticationHelper.AuthenticateDevice( this.Context, deviceToken); } // User logs on. employee = AuthenticationHelper.AuthenticateAndAuthorizeUser(request, device); identity = new CommerceIdentity(employee, device); // If the request is for elevate operation if (request.RetailOperation != RetailOperation.None) { // Add the Elevation properties to the claim. identity.OriginalUserId = this.Context.GetPrincipal().UserId; identity.ElevatedRetailOperation = request.RetailOperation; // successful manager override for operation with id and operator with id var message = string.Format( "Manager with id '{0}' has approved override for operation with id '{1}' to the operator with id '{2}'.", request.StaffId, identity.ElevatedRetailOperation, identity.OriginalUserId); LogAuditEntry(request.RequestContext, "ElevateUser", message); } return(new UserAuthenticationResponse(employee, device, identity)); } catch (Exception exception) { RetailLogger.Log.CrtWorkflowUserAuthenticationRequestHandlerFailure(request.StaffId, deviceId, exception); throw; } }
/// <summary> /// Initializes a new instance of the <see cref="CommerceRuntimeUserToken"/> class. /// </summary> /// <param name="initialIdentity">The initial commerce identity.</param> /// <param name="newIdentity">The new commerce identity.</param> public CommerceRuntimeUserToken(CommerceIdentity initialIdentity, CommerceIdentity newIdentity) : base(CommerceRuntimeTokenSchemeName) { ThrowIf.Null(newIdentity, "newIdentity"); if (initialIdentity == null) { initialIdentity = new CommerceIdentity(); } CommerceRuntimeManager.UpdateCommerceIdentity(initialIdentity, newIdentity); this.commerceRuntimeToken = Newtonsoft.Json.JsonConvert.SerializeObject(initialIdentity); }
/// <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> /// Acquires the user token. /// </summary> /// <param name="userName">Name of the user.</param> /// <param name="password">The password of the user.</param> /// <param name="commerceAuthenticationParameters">The commerce authentication parameters.</param> /// <returns>The user commerce runtime token.</returns> internal override Task <UserToken> AcquireToken(string userName, string password, CommerceAuthenticationParameters commerceAuthenticationParameters) { ThrowIf.Null(commerceAuthenticationParameters, "commerceAuthenticationParameters"); return(Execute <UserToken>(() => { CommerceRuntimeUserToken commerceUserToken; CommerceIdentity originalIdentity = CommerceRuntimeManager.Identity; ConnectionRequest connectionRequest = this.CreateAcquireTokenRequest(userName, password); connectionRequest.Credential = commerceAuthenticationParameters.Credential; connectionRequest.GrantType = commerceAuthenticationParameters.GrantType; connectionRequest.AdditionalAuthenticationData = commerceAuthenticationParameters; LogonCredentials credentials = null; if (!commerceAuthenticationParameters.RetailOperation.HasValue) { try { CommerceIdentity commerceIdentity = new CommerceIdentity(string.Empty, 0, 0, new string[] { }); commerceIdentity.Roles.Add(CommerceRoles.Anonymous); // Set anonymous identity from request. CommerceRuntimeManager.Identity = commerceIdentity; credentials = SecurityManager.Create(CommerceRuntimeManager.Runtime).LogOn(connectionRequest); // Clear the commerce identity. CommerceRuntimeManager.Identity = null; } catch (Exception) { CommerceRuntimeManager.Identity = originalIdentity; throw; } commerceUserToken = new CommerceRuntimeUserToken(credentials.Identity); } else { credentials = SecurityManager.Create(CommerceRuntimeManager.Runtime).ElevateUser(connectionRequest, (RetailOperation)commerceAuthenticationParameters.RetailOperation); commerceUserToken = new CommerceRuntimeUserToken(originalIdentity, credentials.Identity); } return commerceUserToken; })); }
/// <summary> /// Updates the <paramref name="targetIdentity"/> with the fields from the <paramref name="sourceIdentity"/>. /// </summary> /// <param name="targetIdentity">The identity object to be updated.</param> /// <param name="sourceIdentity">The identity object source for the update.</param> internal static void UpdateCommerceIdentity(CommerceIdentity targetIdentity, CommerceIdentity sourceIdentity) { ThrowIf.Null(targetIdentity, "targetIdentity"); ThrowIf.Null(sourceIdentity, "sourceIdentity"); targetIdentity.UserId = sourceIdentity.UserId; if (sourceIdentity.UserId != null) { targetIdentity.Roles.Add(CommerceRoles.Employee); } if (!string.IsNullOrWhiteSpace(sourceIdentity.OriginalUserId)) { targetIdentity.OriginalUserId = sourceIdentity.OriginalUserId; targetIdentity.ElevatedRetailOperation = sourceIdentity.ElevatedRetailOperation; targetIdentity.UserId = sourceIdentity.UserId; const string ManagerPrivilege = "MANAGERPRIVILEGES"; if (sourceIdentity.Roles.Contains(ManagerPrivilege)) { targetIdentity.Roles.Add(ManagerPrivilege); } } else { foreach (var role in sourceIdentity.Roles) { targetIdentity.Roles.Add(role); } } if (!string.IsNullOrWhiteSpace(sourceIdentity.DeviceNumber)) { targetIdentity.DeviceToken = sourceIdentity.DeviceToken; targetIdentity.LogOnConfiguration = sourceIdentity.LogOnConfiguration; targetIdentity.TerminalId = sourceIdentity.TerminalId; targetIdentity.DeviceNumber = sourceIdentity.DeviceNumber; targetIdentity.ChannelId = sourceIdentity.ChannelId; } }
/// <summary> /// Clears the user identity from the <paramref name="currentToken"/> and returns a new <see cref="UserToken"/> without user identity information. /// </summary> /// <param name="currentToken">The current user token.</param> /// <returns>The new user token after user identity is cleared.</returns> internal static UserToken RemoveUserIdentityFromToken(UserToken currentToken) { CommerceIdentity commerceIdentity; if (currentToken == null) { commerceIdentity = new CommerceIdentity(); } else { commerceIdentity = Newtonsoft.Json.JsonConvert.DeserializeObject <CommerceIdentity>(currentToken.Token); } commerceIdentity.UserId = null; commerceIdentity.OriginalUserId = null; commerceIdentity.ElevatedRetailOperation = RetailOperation.None; commerceIdentity.Roles.Clear(); commerceIdentity.Roles.Add(CommerceRoles.Anonymous); return(new CommerceRuntimeUserToken(commerceIdentity)); }
/// <summary> /// Executes the workflow to do user authentication renewal. /// </summary> /// <param name="request">The request.</param> /// <returns>The response.</returns> protected override UserAuthenticationRenewalResponse Process(UserAuthenticationRenewalRequest request) { ThrowIf.Null(request, "request"); Device device = null; // If device Id is present, authenticate the device to check if the device is active. if (!string.IsNullOrWhiteSpace(this.Context.GetPrincipal().DeviceNumber)) { device = AuthenticationHelper.AuthenticateDevice(this.Context, this.Context.GetPrincipal().DeviceToken); } // Send authentication renewal request to the service. Employee employee = AuthenticationHelper.AuthenticateRenewalUser(this.Context, device); CommerceIdentity identity = new CommerceIdentity(employee, device) { // Add the LogOn Configuration to the claim. LogOnConfiguration = this.Context.GetPrincipal().LogOnConfiguration }; return(new UserAuthenticationRenewalResponse(employee, device, identity)); }
/// <summary> /// Initializes a new instance of the <see cref="CommerceRuntimeUserToken"/> class. /// </summary> /// <param name="commerceIdentity">The commerce identity.</param> public CommerceRuntimeUserToken(CommerceIdentity commerceIdentity) : this(null, commerceIdentity) { }
/// <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> /// Default constructor /// </summary> public PriceInquiryController() { // Need to set the ChannelId explicitly this.CommerceIdentity = User.Identity as CommerceIdentity; this.CommerceIdentity.ChannelId = 5637144662; }