public async Task GivenRequestContainingExistingEmail_WhenTheUserAlreadyExists_ThrowsExceptionWithBadRequest() { // Arrange, seed an existing user and new command var createUserCommand = new CreateUserCommand { User = new UserRegistrationDto { Username = "******", Email = "*****@*****.**", Password = "******" } }; var existingUserWithSameUsername = new ConduitUser { Email = "*****@*****.**", NormalizedEmail = "*****@*****.**".ToUpperInvariant(), UserName = "******", NormalizedUserName = "******".ToUpperInvariant(), SecurityStamp = "someRandomSecurityStamp" }; existingUserWithSameUsername.PasswordHash = new PasswordHasher <ConduitUser>() .HashPassword(existingUserWithSameUsername, "password"); await UserManager.CreateAsync(existingUserWithSameUsername); // Act var command = new CreateUserCommandHandler(_logger, UserManager, Context, Mapper, TokenService); // Assert await Should.ThrowAsync <ConduitApiException>(async() => { await command.Handle(createUserCommand, CancellationToken.None); }); }
public async Task <UserViewModel> Handle(CreateUserCommand request, CancellationToken cancellationToken) { // Validate the username is not in use var existingUserByUserName = await _userManager.FindByNameAsync(request.User.Username); if (existingUserByUserName != null) { throw new ConduitApiException($"Username {request.User.Username} is already in use", HttpStatusCode.BadRequest); } // Validate the email is not in use var existingUserByEmail = await _userManager.FindByEmailAsync(request.User.Email); if (existingUserByEmail != null) { throw new ConduitApiException($"Email {request.User.Email} is already in use", HttpStatusCode.BadRequest); } // Instantiate and attempt to create the user var newUser = new ConduitUser { UserName = request.User.Username, Email = request.User.Email, }; var createUserResult = await _userManager.CreateAsync(newUser, request.User.Password); // Instantiate the creation exception and errors, if necessary if (!createUserResult.Succeeded) { var exception = new ConduitApiException($"Could not create user with [{request.User.Username}]", HttpStatusCode.BadRequest); foreach (var error in createUserResult.Errors) { var conduitError = new ConduitApiError(error.Code, error.Description); exception.ApiErrors.Add(conduitError); } throw exception; } // Generate the token and map the user var token = _tokenService.CreateToken(newUser); var userViewModel = new UserViewModel { User = _mapper.Map <UserDto>(newUser) }; userViewModel.User.Token = token; await _context.AddActivityAndSaveChangesAsync(ActivityType.UserCreated, TransactionType.ConduitUser, newUser.Id, cancellationToken); _logger.LogInformation($"{request.User.Username}] created successfully"); return(userViewModel); }
private static ClaimsIdentity BuildUserBasedClaims(ConduitUser user) { var claims = new[] { new Claim(ClaimTypes.Name, user.Id), new Claim(ClaimTypes.Email, user.Email), new Claim(ClaimTypes.UserData, user.UserName), new Claim("username", user.UserName), }; return(new ClaimsIdentity(claims)); }
/// <summary> /// Seeds the user entity using Identity and returns the user's ID to be used in the test relations. /// </summary> /// <param name="context">Conduit database context</param> /// <param name="userId">Out parameter passed to subsequent seeder methods</param> /// <param name="testUserId">Secondary out parameter passed to subsequent seeder methods</param> private static void SeedConduitUsers(ConduitDbContext context, out string userId, out string testUserId) { var testUser1 = new ConduitUser { Email = "*****@*****.**", NormalizedEmail = "*****@*****.**".ToUpperInvariant(), UserName = "******", NormalizedUserName = "******".ToUpperInvariant(), Bio = "Lover of cheap and even cheaper wine.", Image = "https://joeymckenzie.azurewebsites.net/images/me.jpg", SecurityStamp = "someRandomSecurityStamp" }; testUser1.PasswordHash = new PasswordHasher <ConduitUser>() .HashPassword(testUser1, "#password1!"); var testUser2 = new ConduitUser { Email = "*****@*****.**", NormalizedEmail = "*****@*****.**".ToUpperInvariant(), UserName = "******", NormalizedUserName = "******".ToUpperInvariant(), Bio = "I AM NOT A ROBOT.", Image = "https://joeymckenzie.azurewebsites.net/images/me.jpg", SecurityStamp = "someRandomSecurityStamp" }; testUser2.PasswordHash = new PasswordHasher <ConduitUser>() .HashPassword(testUser2, "#passwordTwo1!"); var testUser3 = new ConduitUser { Email = "*****@*****.**", NormalizedEmail = "*****@*****.**".ToUpperInvariant(), UserName = "******", NormalizedUserName = "******".ToUpperInvariant(), Bio = "AGAIN, I AM NOT A ROBOT.", Image = "https://joeymckenzie.azurewebsites.net/images/me.jpg", SecurityStamp = "someRandomSecurityStamp" }; testUser3.PasswordHash = new PasswordHasher <ConduitUser>() .HashPassword(testUser3, "#password3!"); context.Users.Add(testUser1); context.Users.Add(testUser2); context.Users.Add(testUser3); context.SaveChanges(); userId = testUser1.Id; testUserId = testUser2.Id; }
private static IEnumerable <Claim> GetDefaultClaims(ConduitUser user, string issuer, string audience) { return(new[] { new Claim(ClaimTypes.Name, user.Id), new Claim(JwtRegisteredClaimNames.Sub, user.Id), new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim(JwtRegisteredClaimNames.Iss, issuer), new Claim(JwtRegisteredClaimNames.Aud, audience), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim("username", user.UserName) }); }
public static ConduitDbContext Create(out ConduitUser user) { var options = new DbContextOptionsBuilder <ConduitDbContext>() .UseInMemoryDatabase("Brewdude.Application.Tests.Db") .Options; var context = new ConduitDbContext(options); context.Database.EnsureCreated(); ConduitDbInitializer.Initialize(context); user = context.Users.FirstOrDefault(); return(context); }
public string CreateToken(ConduitUser user) { var tokenKey = Encoding.ASCII.GetBytes(_configuration["JWT_SECRET"]); var issuer = _configuration["ISSUER"]; var audience = _configuration["AUDIENCE"]; var securityToken = new JwtSecurityToken( issuer: issuer, audience: audience, claims: GetDefaultClaims(user, issuer, audience), notBefore: _dateTime.Now, expires: _dateTime.Now.AddMinutes(60), signingCredentials: new SigningCredentials(new SymmetricSecurityKey(tokenKey), SecurityAlgorithms.HmacSha256Signature)); return(new JwtSecurityTokenHandler().WriteToken(securityToken)); }
public static UserManager <ConduitUser> Create(ConduitUser user) { var userStoreMock = new Mock <IUserStore <ConduitUser> >(); var userEmailStoreMock = new Mock <IUserEmailStore <ConduitUser> >(); // Setup store mock calls userStoreMock.Setup(s => s.CreateAsync(user, CancellationToken.None)) .Returns(Task.FromResult(IdentityResult.Success)); userEmailStoreMock.Setup(s => s.FindByEmailAsync(user.Email, CancellationToken.None)) .Returns((Task <ConduitUser>)null); var options = new Mock <IOptions <IdentityOptions> >(); var identityOptions = new IdentityOptions { Lockout = { AllowedForNewUsers = false } }; options.Setup(o => o.Value).Returns(identityOptions); var userValidators = new List <IUserValidator <ConduitUser> >(); var validator = new Mock <IUserValidator <ConduitUser> >(); userValidators.Add(validator.Object); var passwordValidators = new List <PasswordValidator <ConduitUser> > { new PasswordValidator <ConduitUser>() }; var userManager = new UserManager <ConduitUser>( userEmailStoreMock.Object, options.Object, new PasswordHasher <ConduitUser>(), userValidators, passwordValidators, new UpperInvariantLookupNormalizer(), new IdentityErrorDescriber(), null, NullLogger <UserManager <ConduitUser> > .Instance); validator.Setup(v => v.ValidateAsync(userManager, It.IsAny <ConduitUser>())) .Returns(Task.FromResult(IdentityResult.Success)).Verifiable(); return(userManager); }
public string CreateToken(ConduitUser user) { return("aSecurityToken"); }
private static void SetFavoritedFollowingAndArticleTags(ArticleDto article, Article articleEntity, ConduitUser user, IList <ArticleTag> articleTags) { // Get the associated article tags var associatedTags = articleTags? .Where(at => at.ArticleId == articleEntity?.Id) .Select(at => at.Tag.Description); // Set the following and favorited properties if (articleEntity != null && user != null) { article.Author.Following = articleEntity.Author.Followers.Any(f => f.UserFollower == user); article.Favorited = articleEntity.Favorites.Any(f => f.User == user); article.TagList = associatedTags; } }
public static void SetViewModelPropertiesForArticle(this ArticleViewModel articleViewModel, ArticleDto article, Article articleEntity, IList <ArticleTag> articleTags, ConduitUser user) { SetFavoritedFollowingAndArticleTags(article, articleEntity, user, articleTags); }
public static void SetViewModelProperties(this ArticleViewModelList articleViewModelList, IList <Article> articles, ConduitUser user, IList <ArticleTag> articleTags) { foreach (var article in articleViewModelList.Articles) { // Retrieve the corresponding article var mappedArticleEntity = articles.FirstOrDefault(a => string.Equals(a.Title, article.Title, StringComparison.OrdinalIgnoreCase)); SetFavoritedFollowingAndArticleTags(article, mappedArticleEntity, user, articleTags); } }
public async Task <ArticleViewModelList> Handle(GetArticlesQuery request, CancellationToken cancellationToken) { // Retrieve all articles from the database and include the corresponding var articles = _context.Articles .Include(a => a.Author) .ThenInclude(au => au.Followers) .Include(a => a.ArticleTags) .ThenInclude(at => at.Tag) .Include(a => a.Favorites) .ThenInclude(f => f.User) .AsQueryable(); // Instantiate the user and empty results list ConduitUser author = null; var noSearchResults = new ArticleViewModelList { Articles = new List <ArticleDto>() }; // Filter on author if (!string.IsNullOrWhiteSpace(request.Author)) { author = await _userManager.FindByNameAsync(request.Author); // If no author is found during the search, return an empty list if (author != null) { articles = articles.Where(a => a.Author == author); } else { return(noSearchResults); } } // Filter on tags if (!string.IsNullOrWhiteSpace(request.Tag)) { var tag = await _context.Tags .FirstOrDefaultAsync(t => string.Equals(t.Description, request.Tag, StringComparison.OrdinalIgnoreCase), cancellationToken); // If no tag is found for the requesting tag, return an empty list if (tag != null) { articles = articles.Where(a => a.ArticleTags.Select(at => at.Tag).Contains(tag)); } else { return(noSearchResults); } } // Filter on favorited if (!string.IsNullOrWhiteSpace(request.Favorited)) { author = author ?? await _userManager.FindByNameAsync(request.Favorited); // If no favorited articles are found by the user, return an empty list if (author != null && articles.Any(a => a.Favorites.Select(f => f.User).Contains(author))) { articles = articles.Where(a => a.Favorites.Select(f => f.User).Contains(author)); } else { return(noSearchResults); } } // Paginate and map the results var results = await articles .Skip(request.Offset) .Take(request.Limit) .OrderByDescending(a => a.CreatedAt) .ToListAsync(cancellationToken); var articlesViewModelList = new ArticleViewModelList { Articles = _mapper.Map <IEnumerable <ArticleDto> >(results) }; return(articlesViewModelList); }