private bool UserExistsInLdapDirectory(LdapSettings ldapSettings, LoginInfo loginInfo) { var userName = loginInfo.UserName != null?loginInfo.UserName.Trim() : loginInfo.Login; var filter = I18NHelper.FormatInvariant("(&(objectCategory={0})({1}={2}))", ldapSettings.GetEffectiveUserObjectCategoryAttribute(), ldapSettings.GetEffectiveAccountNameAttribute(), LdapHelper.EscapeLdapSearchFilter(userName)); try { var found = _authenticator.SearchLdap(ldapSettings, filter); if (found) { return(true); } _log.LogInformation(LogSourceLdap, I18NHelper.FormatInvariant("User '{0}' is not found in LDAP directory {1}", userName, ldapSettings.LdapAuthenticationUrl)); return(false); } catch (Exception ex) { _log.LogInformation(LogSourceLdap, I18NHelper.FormatInvariant("Error while searching a user in LDAP directory {0}", ldapSettings.LdapAuthenticationUrl)); _log.LogError(LogSourceLdap, ex); return(false); } }
public async Task <AuthenticationUser> AuthenticateUserAsync(string login, string password, bool ignoreInvalidLoginAttempts) { if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password)) { throw new AuthenticationException("Username and password cannot be empty", ErrorCodes.EmptyCredentials); } var user = await _userRepository.GetUserByLoginAsync(login); if (user == null) { await _log.LogInformation(WebApiConfig.LogSourceSessions, I18NHelper.FormatInvariant("Could not get user with login '{0}'", login)); throw new AuthenticationException("Invalid username or password", ErrorCodes.InvalidCredentials); } var instanceSettings = await _settingsRepository.GetInstanceSettingsAsync(); if (instanceSettings.IsSamlEnabled.GetValueOrDefault()) { // Fallback is allowed by default (value is null) if (!user.IsFallbackAllowed.GetValueOrDefault(true)) { throw new AuthenticationException("User must be authenticated via Federated Authentication mechanism", ErrorCodes.FallbackIsDisabled); } } AuthenticationStatus authenticationStatus; switch (user.Source) { case UserGroupSource.Database: authenticationStatus = AuthenticateDatabaseUser(user, password, instanceSettings.PasswordExpirationInDays); break; case UserGroupSource.Windows: if (!instanceSettings.IsLdapIntegrationEnabled) { throw new AuthenticationException(string.Format("To authenticate user with login: {0}, ldap integration must be enabled", login), ErrorCodes.LdapIsDisabled); } authenticationStatus = await _ldapRepository.AuthenticateLdapUserAsync(login, password, instanceSettings.UseDefaultConnection); break; default: throw new AuthenticationException(string.Format("Authentication provider could not be found for login: {0}", login)); } await ProcessAuthenticationStatus(authenticationStatus, user, instanceSettings, ignoreInvalidLoginAttempts); user.LicenseType = await _userRepository.GetEffectiveUserLicenseAsync(user.Id); return(user); }
internal Task LoadAsync() { return(Task.Run(async() => { try { await _log.LogInformation(WebApiConfig.LogSourceSessions, "Service starting..."); var ps = 1000; var pn = 1; int count; var expiredSessions = new List <Session>(); do { count = 0; var sessions = await _repo.SelectSessions(ps, pn); foreach (var session in sessions) { ++count; if (session.IsExpired()) { expiredSessions.Add(session); } else { InsertSession(session); } } ++pn; }while (count == ps); // Mark expired sessions as trully expired in the DB foreach (var session in expiredSessions) { await _repo.EndSession(session.SessionId, session.EndTime); } var logExpiredSessionsCount = expiredSessions.Count > 0 ? $" {expiredSessions.Count} sessions expired" : string.Empty; await _log.LogInformation(WebApiConfig.LogSourceSessions, $"Service started.{logExpiredSessionsCount}"); } catch (Exception ex) { await _log.LogError(WebApiConfig.LogSourceSessions, new Exception("Error loading sessions from database.", ex)); } })); }
public static async Task ProcessMessages(string logSource, TenantInformation tenant, IServiceLogRepository serviceLogRepository, IList <IWorkflowMessage> messages, string exceptionMessagePrepender, IWorkflowMessagingProcessor workflowMessagingProcessor) { if (messages == null || messages.Count <= 0) { return; } var processor = workflowMessagingProcessor ?? WorkflowMessagingProcessor.Instance; foreach (var actionMessage in messages.Where(a => a != null)) { try { await ActionMessageSender.Send((ActionMessage)actionMessage, tenant, processor); string message = $"Sent {actionMessage.ActionType} message: {actionMessage.ToJSON()} with tenant id: {tenant.TenantId} to the Message queue"; await serviceLogRepository.LogInformation(logSource, message); } catch (Exception ex) { string message = $"Error while sending {actionMessage.ActionType} message with content {actionMessage.ToJSON()}. {exceptionMessagePrepender}. Exception: {ex.Message}. StackTrace: {ex.StackTrace ?? string.Empty}"; await serviceLogRepository.LogError(logSource, message); throw; } } }
public async Task <IHttpActionResult> PostSession(string login, [FromBody] string password, bool force = false) { try { var decodedLogin = SystemEncryptions.Decode(login); var decodedPassword = SystemEncryptions.Decode(password); var user = await _authenticationRepository.AuthenticateUserAsync(decodedLogin, decodedPassword, false); return(await RequestSessionTokenAsync(user, force)); } catch (AuthenticationException ex) { await _log.LogInformation(WebApiConfig.LogSourceSessions, ex.Message); throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex.CreateHttpError())); } catch (ApplicationException ex) { await _log.LogInformation(WebApiConfig.LogSourceSessions, ex.Message); return(Conflict()); } catch (ArgumentNullException ex) { await _log.LogInformation(WebApiConfig.LogSourceSessions, ex.Message); return(BadRequest()); } catch (FormatException ex) { await _log.LogInformation(WebApiConfig.LogSourceSessions, ex.Message); throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex.Message)); } catch (Exception ex) { await _log.LogError(WebApiConfig.LogSourceSessions, ex); return(InternalServerError()); } }
public async Task ProcessMessages(string logSource, IApplicationSettingsRepository applicationSettingsRepository, IServiceLogRepository serviceLogRepository, IList <IWorkflowMessage> messages, string exceptionMessagePrepender, IDbTransaction transaction = null) { var tenantInfo = await applicationSettingsRepository.GetTenantInfo(transaction); if (string.IsNullOrWhiteSpace(tenantInfo?.TenantId)) { throw new TenantInfoNotFoundException("No tenant information found. Please contact your administrator."); } if (messages == null || messages.Count <= 0) { return; } foreach (var actionMessage in messages.Where(a => a != null)) { try { await WorkflowMessagingProcessor.Instance.SendMessageAsync(tenantInfo.TenantId, actionMessage); string message = $"Sent {actionMessage.ActionType} message: {actionMessage.ToJSON()} with tenant id: {tenantInfo.TenantId} to the Message queue"; await serviceLogRepository.LogInformation(logSource, message); } catch (Exception ex) { string message = $"Error while sending {actionMessage.ActionType} message with content {actionMessage.ToJSON()}. {exceptionMessagePrepender}. Exception: {ex.Message}. StackTrace: {ex.StackTrace ?? string.Empty}"; await serviceLogRepository.LogError(logSource, message); throw; } } }
public async Task Execute(IApplicationSettingsRepository applicationSettingsRepository, IServiceLogRepository serviceLogRepository, ActionMessage message, IDbTransaction transaction) { var tenantInfo = await applicationSettingsRepository.GetTenantInfo(transaction); var tenantId = tenantInfo?.TenantId; try { if (string.IsNullOrWhiteSpace(tenantId)) { throw new TenantInfoNotFoundException("No tenant information found. Please contact your administrator."); } await WorkflowMessagingProcessor.Instance.SendMessageAsync(tenantId, message); await serviceLogRepository.LogInformation("SendMessageExecutor", $"Sent {message.ActionType} message for tenant {tenantId}: {message.ToJSON()}"); } catch (Exception ex) { await serviceLogRepository.LogError("SendMessageExecutor", $"Failed to send {message.ActionType} message for tenant {tenantId}: {message.ToJSON()}. Exception: {ex.Message}"); throw; } }
public static async Task <IList <IWorkflowMessage> > GenerateMessages(int userId, int revisionId, string userName, long transactionId, WorkflowEventTriggers postOpTriggers, IBaseArtifactVersionControlInfo artifactInfo, string projectName, IDictionary <int, IList <Property> > modifiedProperties, WorkflowState currentState, string artifactUrl, string baseUrl, IEnumerable <int> ancestorArtifactTypeIds, IUsersRepository usersRepository, IServiceLogRepository serviceLogRepository, IWebhooksRepository webhooksRepository, IProjectMetaRepository projectMetaRepository) { var resultMessages = new List <IWorkflowMessage>(); var baseHostUri = baseUrl ?? ServerUriHelper.GetBaseHostUri()?.ToString(); foreach (var workflowEventTrigger in postOpTriggers) { if (workflowEventTrigger?.Action == null) { continue; } switch (workflowEventTrigger.ActionType) { case MessageActionType.Notification: var notificationAction = workflowEventTrigger.Action as EmailNotificationAction; if (notificationAction == null) { continue; } var notificationMessage = await GetNotificationMessage(userId, revisionId, transactionId, artifactInfo, projectName, notificationAction, artifactUrl, baseHostUri, usersRepository); if (notificationMessage == null) { await serviceLogRepository.LogInformation(LogSource, $"Skipping Email notification action for artifact {artifactInfo.Id}"); Log.Debug($" Skipping Email notification action for artifact {artifactInfo.Id}. Message: Notification."); continue; } resultMessages.Add(notificationMessage); break; case MessageActionType.GenerateChildren: var generateChildrenAction = workflowEventTrigger.Action as GenerateChildrenAction; if (generateChildrenAction == null) { continue; } var ancestors = new List <int>(ancestorArtifactTypeIds ?? new int[0]); ancestors.Add(artifactInfo.ItemTypeId); var generateChildrenMessage = new GenerateDescendantsMessage { TransactionId = transactionId, ChildCount = generateChildrenAction.ChildCount.GetValueOrDefault(10), DesiredArtifactTypeId = generateChildrenAction.ArtifactTypeId, ArtifactId = artifactInfo.Id, AncestorArtifactTypeIds = ancestors, RevisionId = revisionId, UserId = userId, ProjectId = artifactInfo.ProjectId, UserName = userName, BaseHostUri = baseHostUri, ProjectName = projectName, TypePredefined = (int)artifactInfo.PredefinedType }; resultMessages.Add(generateChildrenMessage); break; case MessageActionType.GenerateTests: var generateTestsAction = workflowEventTrigger.Action as GenerateTestCasesAction; if (generateTestsAction == null || artifactInfo.PredefinedType != ItemTypePredefined.Process) { await serviceLogRepository.LogInformation(LogSource, $"Skipping GenerateTestCasesAction for artifact {artifactInfo.Id} as it is not a process"); Log.Debug($"Skipping GenerateTestCasesAction for artifact {artifactInfo.Id} as it is not a process. Message: Notification."); continue; } var generateTestsMessage = new GenerateTestsMessage { TransactionId = transactionId, ArtifactId = artifactInfo.Id, RevisionId = revisionId, UserId = userId, ProjectId = artifactInfo.ProjectId, UserName = userName, BaseHostUri = baseHostUri, ProjectName = projectName }; resultMessages.Add(generateTestsMessage); break; case MessageActionType.GenerateUserStories: var generateUserStories = workflowEventTrigger.Action as GenerateUserStoriesAction; if (generateUserStories == null || artifactInfo.PredefinedType != ItemTypePredefined.Process) { await serviceLogRepository.LogInformation(LogSource, $"Skipping GenerateUserStories for artifact {artifactInfo.Id} as it is not a process"); Log.Debug($"Skipping GenerateUserStories for artifact {artifactInfo.Id} as it is not a process. Message: Notification."); continue; } var generateUserStoriesMessage = new GenerateUserStoriesMessage { TransactionId = transactionId, ArtifactId = artifactInfo.Id, RevisionId = revisionId, UserId = userId, ProjectId = artifactInfo.ProjectId, UserName = userName, BaseHostUri = baseHostUri, ProjectName = projectName }; resultMessages.Add(generateUserStoriesMessage); break; case MessageActionType.Webhooks: var webhookAction = workflowEventTrigger.Action as WebhookAction; if (webhookAction == null) { continue; } var customTypes = await projectMetaRepository.GetCustomProjectTypesAsync(artifactInfo.ProjectId, userId); var artifactType = customTypes.ArtifactTypes.FirstOrDefault(at => at.Id == artifactInfo.ItemTypeId); var artifactPropertyInfos = await webhooksRepository.GetArtifactsWithPropertyValuesAsync( userId, new List <int> { artifactInfo.Id }, new List <int> { (int)PropertyTypePredefined.Name, (int)PropertyTypePredefined.Description, (int)PropertyTypePredefined.ID, (int)PropertyTypePredefined.CreatedBy, (int)PropertyTypePredefined.LastEditedOn, (int)PropertyTypePredefined.LastEditedBy, (int)PropertyTypePredefined.CreatedOn }, artifactType.CustomPropertyTypeIds); var webhookArtifactInfo = new WebhookArtifactInfo { Id = Guid.NewGuid().ToString(), EventType = WebhookEventType, PublisherId = WebhookPublisherId, Scope = new WebhookArtifactInfoScope { Type = WebhookType, WorkflowId = currentState.WorkflowId }, Resource = new WebhookResource { Name = artifactInfo.Name, ProjectId = artifactInfo.ProjectId, ParentId = ((WorkflowMessageArtifactInfo)artifactInfo).ParentId, ArtifactTypeId = artifactInfo.ItemTypeId, ArtifactTypeName = artifactType?.Name, BaseArtifactType = artifactType?.PredefinedType?.ToString(), ArtifactPropertyInfo = await ConvertToWebhookPropertyInfo(artifactPropertyInfos, customTypes.PropertyTypes, usersRepository), State = new WebhookStateInfo { Id = currentState.Id, Name = currentState.Name, WorkflowId = currentState.WorkflowId }, Revision = revisionId, Version = WebhookArtifactVersion, Id = artifactInfo.Id, BlueprintUrl = string.Format($"{baseHostUri}?ArtifactId={artifactInfo.Id}"), Link = string.Format($"{baseHostUri}api/v1/projects/{artifactInfo.ProjectId}/artifacts/{artifactInfo.Id}") } }; var webhookMessage = await GetWebhookMessage(userId, revisionId, transactionId, webhookAction, webhooksRepository, webhookArtifactInfo); if (webhookMessage == null) { await serviceLogRepository.LogInformation(LogSource, $"Skipping Webhook action for artifact {artifactInfo.Id}: {artifactInfo.Name}."); continue; } resultMessages.Add(webhookMessage); break; } } return(resultMessages); }
internal static async Task <bool> ProcessCreatedArtifacts(TenantInformation tenant, ArtifactsPublishedMessage message, IArtifactsPublishedRepository repository, IServiceLogRepository serviceLogRepository, IWorkflowMessagingProcessor messageProcessor, int transactionCommitWaitTimeInMilliSeconds = 60000) { var createdArtifacts = message?.Artifacts?.Where(p => p.IsFirstTimePublished && repository.WorkflowRepository.IsWorkflowSupported((ItemTypePredefined)p.Predefined)).ToList(); if (createdArtifacts == null || createdArtifacts.Count <= 0) { Logger.Log("No created artifacts found", message, tenant); return(false); } Logger.Log($"{createdArtifacts.Count} created artifacts found", message, tenant); var artifactIds = createdArtifacts.Select(a => a.Id).ToHashSet(); var artifactInfos = (await repository.WorkflowRepository.GetWorkflowMessageArtifactInfoAsync(message.UserId, artifactIds, message.RevisionId)).ToDictionary(k => k.Id); Logger.Log($"{artifactInfos.Count} artifact infos found", message, tenant); var notificationMessages = new Dictionary <int, List <IWorkflowMessage> >(); foreach (var createdArtifact in createdArtifacts) { WorkflowMessageArtifactInfo artifactInfo; if (!artifactInfos.TryGetValue(createdArtifact.Id, out artifactInfo)) { await serviceLogRepository.LogInformation(LogSource, $"Could not recover information for artifact Id: {createdArtifact.Id} and Name: {createdArtifact.Name} and Project Id: {createdArtifact.ProjectId}"); Logger.Log($"Could not recover information for artifact Id: {createdArtifact.Id} and Name: {createdArtifact.Name} and Project Id: {createdArtifact.ProjectId}", message, tenant); continue; } var eventTriggers = await repository.WorkflowRepository.GetWorkflowEventTriggersForNewArtifactEvent(message.UserId, new[] { createdArtifact.Id }, message.RevisionId, true); if (eventTriggers?.AsynchronousTriggers == null || eventTriggers.AsynchronousTriggers.Count == 0) { Logger.Log($"Found no async triggers for artifact with ID {createdArtifact.Id}", message, tenant); continue; } Logger.Log($"Found {eventTriggers.AsynchronousTriggers.Count} async triggers for artifact with ID {createdArtifact.Id}", message, tenant); int artifactId = createdArtifact.Id; var currentState = await repository.WorkflowRepository.GetStateForArtifactAsync(message.UserId, createdArtifact.Id, int.MaxValue, true); var actionMessages = await WorkflowEventsMessagesHelper.GenerateMessages(message.UserId, message.RevisionId, message.UserName, message.TransactionId, eventTriggers.AsynchronousTriggers, artifactInfo, artifactInfo.ProjectName, new Dictionary <int, IList <Property> >(), currentState, createdArtifact.Url, createdArtifact.BaseUrl, createdArtifact.AncestorArtifactTypeIds, repository.UsersRepository, serviceLogRepository, repository.WebhooksRepository, repository.ProjectMetaRepository); if (actionMessages == null || actionMessages.Count == 0) { continue; } if (!notificationMessages.ContainsKey(artifactId)) { notificationMessages.Add(artifactId, new List <IWorkflowMessage>()); } notificationMessages[artifactId].AddRange(actionMessages); } if (notificationMessages.Count == 0) { Logger.Log("None of the created artifacts have async triggers", message, tenant); return(false); } Logger.Log($"Sending async trigger messages for artifacts: {string.Join(", ", notificationMessages.Select(kvp => kvp.Key))}", message, tenant); foreach (var notificationMessage in notificationMessages.Where(m => m.Value != null)) { await WorkflowEventsMessagesHelper.ProcessMessages(LogSource, tenant, serviceLogRepository, notificationMessage.Value, $"Error on new artifact creation with Id: {notificationMessage.Key}", messageProcessor); } return(true); }
public async Task <IHttpActionResult> PostRequestPasswordResetAsync([FromBody] string login) { try { var matchingSetting = await _applicationSettingsRepository.GetValue(IsPasswordRecoveryEnabledKey, false); if (!matchingSetting) { await _log.LogInformation(WebApiConfig.LogSourceUsersPasswordReset, "Password recovery is disabled"); return(Conflict()); } var instanceSettings = await _settingsRepository.GetInstanceSettingsAsync(); if (instanceSettings?.EmailSettingsDeserialized?.HostName == null) { await _log.LogInformation(WebApiConfig.LogSourceUsersPasswordReset, "Invalid instance email settings"); return(Conflict()); } var user = await _userRepository.GetUserByLoginAsync(login); if (user == null) { await _log.LogInformation(WebApiConfig.LogSourceUsersPasswordReset, "The user doesn't exist"); return(Conflict()); } bool passwordResetAllowed = await _userRepository.CanUserResetPasswordAsync(login); if (!passwordResetAllowed) { await _log.LogInformation(WebApiConfig.LogSourceUsersPasswordReset, "The user isn't allowed to reset the password"); return(Conflict()); } bool passwordRequestLimitExceeded = await _userRepository.HasUserExceededPasswordRequestLimitAsync(login); if (passwordRequestLimitExceeded) { await _log.LogInformation(WebApiConfig.LogSourceUsersPasswordReset, "Exceeded requests limit"); return(Conflict()); } bool passwordResetCooldownInEffect = await _authenticationRepository.IsChangePasswordCooldownInEffect(user); if (passwordResetCooldownInEffect) { await _log.LogInformation(WebApiConfig.LogSourceUsersPasswordReset, "Cooldown is in effect"); return(Conflict()); } var recoveryToken = SystemEncryptions.CreateCryptographicallySecureGuid(); var recoveryUrl = new Uri(Request.RequestUri, ServiceConstants.ForgotPasswordResetUrl + "/" + recoveryToken).AbsoluteUri; // decrypt the password to be set in mailBee instanceSettings.EmailSettingsDeserialized.DecryptPassword(); _emailHelper.Initialize(instanceSettings.EmailSettingsDeserialized); _emailHelper.SendEmail(user.Email, "Reset Password", $@" <html> <div>Hello {user.DisplayName}.</div> <br> <div>We have received a request to reset your password.</div> <br> <div>To confirm this password reset, visit the following link:</div> <a href='{recoveryUrl}'>Reset password</a> <br><br> <div>If you did not make this request, you can ignore this email, and no changes will be made.</div> <br> <div>If you have any questions, please contact your administrator. </div> </html>"); await _userRepository.UpdatePasswordRecoveryTokensAsync(login, recoveryToken); return(Ok()); } catch (EmailException ex) { await _log.LogError(WebApiConfig.LogSourceUsersPasswordReset, ex); return(Conflict()); } catch (Exception ex) { await _log.LogError(WebApiConfig.LogSourceUsersPasswordReset, ex); return(InternalServerError()); } }