public HttpResponseMessage Send(SendConfirmationEmailRequest request) { Guid activityId = Guid.NewGuid(); var response = new ConfirmApiResponse { ActivityId = activityId }; this.ValidateSendInput(request, activityId, response); EntityType entityType; if (!this.TryConvertEntityType(request.ConfirmationType, out entityType)) { Log.Verbose(activityId, "Bad Request: unknown confirmation type: {0}", request.ConfirmationType); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } try { Log.Info(activityId, "Start Processing link account request. uh={0}; confirmation type={1}", request.UserIdHash, entityType); var confirmationEntity = this.userDal.GetConfirmationEntity(request.UserIdHash, entityType); User user = null; if (confirmationEntity != null) { user = this.userDal.GetUserByUserId(confirmationEntity.UserId); } if (confirmationEntity == null || user == null) { response.Code = SendConfirmationResultCode.Invalid.ToString(); Log.Info(activityId, "confirmation entity or user not found to the given user hash and entity type. uh={0}; confirmation type={1}", request.UserIdHash, entityType); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } this.ValidateResendJobsInTimeWindow(user.Id, entityType, activityId, response); ConfirmationEmailCargo job = null; if (entityType == EntityType.AccountLink) { job = this.CreateSendLinkAccountEmailJob(user, entityType, activityId, response); } else if (entityType == EntityType.UnAuthenticatedEmailAddress) { job = this.CreateSendConfirmEmailAddressEmailJob(user, confirmationEntity, entityType, activityId, response); } this.userDal.LogUserConfirmEmailResend(user.Id, entityType); this.confirmationJobsQueue.Enqueue(job); response.Code = SendConfirmationResultCode.EmailSent.ToString(); return(Request.CreateResponse(HttpStatusCode.Accepted, response)); } catch (HttpResponseException) { throw; } catch (Exception e) { Log.Error(activityId, e, "Unexpected error in send confirmation email call. user id hash: {0}; entity type: {1}", request.UserIdHash, entityType); response.Code = SendConfirmationResultCode.UnknownError.ToString(); return(Request.CreateResponse(HttpStatusCode.InternalServerError, response)); } }
/// <summary> The create send link account email job. </summary> /// <param name="user"> The user. </param> /// <param name="entityType"> The entity type. </param> /// <param name="activityId"> The activity id. </param> /// <param name="response"> The response. </param> /// <returns> The <see cref="ConfirmationEmailCargo"/>. </returns> /// <exception cref="HttpResponseException">the account is already linked </exception> private ConfirmationEmailCargo CreateSendLinkAccountEmailJob(User user, EntityType entityType, Guid activityId, ConfirmApiResponse response) { if (!string.IsNullOrEmpty(user.MsId)) { response.Code = SendConfirmationResultCode.AlreadyLinked.ToString(); Log.Info(activityId, "Account is already linked to ms-id/ fb-id, won't resend confirm email. user id={0}; ", user.Id); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } Tuple <string, int> confirmationCode = this.userDal.CreateConfirmationCode(user.Id.ToString(), entityType, user.Id); return(new ConfirmationEmailCargo { Id = Guid.NewGuid(), EntityType = EntityType.AccountLink, EmailAddress = user.Email, UserIdHash = confirmationCode.Item1, ConfirmationCode = confirmationCode.Item2, }); }
/// <summary> The create send confirm email address email job. </summary> /// <param name="user"> The user. </param> /// <param name="confirmEntity"> The confirm entity. </param> /// <param name="entityType"> The entity type. </param> /// <param name="activityId"> The activity id. </param> /// <param name="response"> The response. </param> /// <returns> The <see cref="ConfirmationEmailCargo"/>. the created job to queue</returns> /// <exception cref="HttpResponseException">the email address is already confirmed </exception> private ConfirmationEmailCargo CreateSendConfirmEmailAddressEmailJob(User user, ConfirmEntity confirmEntity, EntityType entityType, Guid activityId, ConfirmApiResponse response) { string emailToConfirm = confirmEntity.Name; if (user.Email == emailToConfirm && user.IsEmailConfirmed) { response.Code = SendConfirmationResultCode.AlreadyConfirmed.ToString(); Log.Info(activityId, "Email Address already confirmed, won't resend confirm email. user id={0}; ", user.Id); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } Tuple <string, int> confirmationCode = this.userDal.CreateConfirmationCode(emailToConfirm, entityType, user.Id); return(new ConfirmationEmailCargo { Id = Guid.NewGuid(), EntityType = EntityType.UnAuthenticatedEmailAddress, EmailAddress = user.Email, UserIdHash = confirmationCode.Item1, ConfirmationCode = confirmationCode.Item2 }); }
/// <summary> The validate resend jobs in time window. </summary> /// <param name="userId"> The user id. </param> /// <param name="entityType"> The entity type. </param> /// <param name="activityId"> The activity id. </param> /// <param name="response"> The response. </param> /// <exception cref="HttpResponseException"> validation failed /// </exception> private void ValidateResendJobsInTimeWindow(Guid userId, EntityType entityType, Guid activityId, ConfirmApiResponse response) { int sendRequestInWindow = this.userDal.GetUserConfirmEmailResendCount(userId, entityType, DateTime.UtcNow.Add(-SendRequestsValidationWindow)); if (sendRequestInWindow >= MaxSendRequestsPerUserInWindow) { response.Code = SendConfirmationResultCode.Invalid.ToString(); Log.Error(activityId, "number of resend confirmation email request passed the allowed number of requests. user id={0}; num of requests={1}", sendRequestInWindow, userId); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } }
/// <summary> The validate send input. </summary> /// <param name="request"> The request. </param> /// <param name="activityId"> The activity id. </param> /// <param name="response"> The response. </param> /// <exception cref="HttpResponseException"> Validation failed /// </exception> private void ValidateSendInput(SendConfirmationEmailRequest request, Guid activityId, ConfirmApiResponse response) { if (request == null) { Log.Verbose(activityId, "Bad Request: input is null"); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } if (string.IsNullOrEmpty(request.UserIdHash)) { Log.Verbose(activityId, "Bad Request: user id is null or empty"); throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, response)); } }
public HttpResponseMessage Link(LinkAccountRequest request) { if (request == null) { throw new HttpResponseException(HttpStatusCode.BadRequest); } string uh = request.UserIdHash; int code = request.Code; Guid activityId = Guid.NewGuid(); var linkAccountResponse = new ConfirmApiResponse { ActivityId = activityId }; Log.Info(activityId, "Start Processing link account request. uh={0}", uh); var identity = Thread.CurrentPrincipal.Identity as CustomIdentity; if (identity == null) { Log.Error(activityId, "call passed authentication however identity context is empty. uh: {0}", uh); linkAccountResponse.Code = LinkAccountResultCode.UnknownError.ToString(); return(Request.CreateResponse(HttpStatusCode.InternalServerError, linkAccountResponse)); } try { var result = this.userDal.ConfirmEntity(uh, EntityType.AccountLink, code); // couldn't confirm code if (result == null || result.Status != ConfirmStatus.CodeConfirmed) { Log.Error(activityId, "Couldn't link account for user: {0}. Response from confirm code call: {1}", uh, result != null ? (ConfirmStatus?)result.Status : null); linkAccountResponse.Code = LinkAccountResultCode.Invalid.ToString(); return(Request.CreateResponse(HttpStatusCode.BadRequest, linkAccountResponse)); } // code confirmed Guid userId = result.UserId.Value; User user = this.userDal.GetUserByUserId(userId); if (user == null) { Log.Error(activityId, "Link account error. User is confirmed however the user couldn't be found in DB. user id: {0}", userId); linkAccountResponse.Code = LinkAccountResultCode.UnknownError.ToString(); return(Request.CreateResponse(HttpStatusCode.InternalServerError, linkAccountResponse)); } User authenticateUser = this.userDal.GetUserByExternalId(identity.ExternalUserId, UserExternalIdType.MsId); // there is a user in the db with the given external id if (authenticateUser != null) { // the users are actualy the same user if (authenticateUser.Id == userId) { Log.Verbose(activityId, "Link account succeeded. User already linked to this external id. user id: {0}", userId); linkAccountResponse.Code = LinkAccountResultCode.Linked.ToString(); return(Request.CreateResponse(HttpStatusCode.Accepted, linkAccountResponse)); } // this external identity already in use. Log.Warn(activityId, "Can't link account to external identity. this identity is in use by other user. user id:{0}, other user id:{1}", userId, authenticateUser.Id); linkAccountResponse.Code = LinkAccountResultCode.ExternalIdentityInUse.ToString(); return(Request.CreateResponse(HttpStatusCode.Forbidden, linkAccountResponse)); } if (user.MsId != null) { // user already linked to different msid Log.Warn(activityId, "User linked to other external id. user id: {0}", userId); linkAccountResponse.Code = LinkAccountResultCode.AlreadyLinked.ToString(); return(Request.CreateResponse(HttpStatusCode.Conflict, linkAccountResponse)); } // Link exterenal id to the account this.userDal.CreateOrGetUserByMsId(identity.ExternalUserId, null, null, userId); Log.Verbose(activityId, "Account linking succeeded. user id: {0}", userId); linkAccountResponse.Code = LinkAccountResultCode.Linked.ToString(); return(Request.CreateResponse(HttpStatusCode.Accepted, linkAccountResponse)); } catch (Exception e) { Log.Error(activityId, e, "Unexpected error in link accounts call. user id hash: {0}", uh); linkAccountResponse.Code = LinkAccountResultCode.UnknownError.ToString(); return(Request.CreateResponse(HttpStatusCode.InternalServerError, linkAccountResponse)); } }