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);
            });
        }
예제 #2
0
        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));
        }
예제 #4
0
        /// <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)
     });
 }
예제 #6
0
        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));
        }
예제 #8
0
        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");
 }
예제 #10
0
        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;
            }
        }
예제 #11
0
 public static void SetViewModelPropertiesForArticle(this ArticleViewModel articleViewModel, ArticleDto article, Article articleEntity, IList <ArticleTag> articleTags, ConduitUser user)
 {
     SetFavoritedFollowingAndArticleTags(article, articleEntity, user, articleTags);
 }
예제 #12
0
 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);
        }