├── Voyage.Web ├── FILE.txt ├── Views │ ├── _ViewStart.cshtml │ ├── Shared │ │ └── Error.cshtml │ ├── Home │ │ └── Index.cshtml │ └── OAuth │ │ └── AuthorizeError.cshtml ├── Global.asax ├── Content │ ├── Images │ │ └── favicon.ico │ ├── ripples.css │ └── Site.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── Models │ └── LoginModel.cs ├── Constants.cs ├── Startup.cs ├── App_Start │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ ├── BundleConfig.cs │ ├── Startup.Auth.cs │ └── WebApiConfig.cs ├── Controllers │ └── HomeController.cs ├── Extensions │ └── ModelStateExtensions.cs ├── AuthProviders │ └── IdentityProvider.cs ├── Filters │ ├── ApiExceptionFilterAttribute.cs │ └── ValidateModelAttribute.cs ├── Global.asax.cs ├── Middleware │ ├── Processors │ │ ├── ErrorResponseProcessor.cs │ │ └── ResponseProcessor.cs │ └── RewindResponseMiddleware.cs ├── Web.Debug.config └── Properties │ └── AssemblyInfo.cs ├── Voyage.Api ├── File.txt ├── Global.asax ├── Hubs │ └── NotificationHub.cs ├── Service References │ └── Application Insights │ │ └── ConnectedService.json ├── App_Start │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ └── WebApiConfig.cs ├── Constants.cs ├── Startup.cs ├── Global.asax.cs ├── Extensions │ └── ModelStateExtensions.cs ├── AuthProviders │ ├── IdentityProvider.cs │ ├── QueryStringOAuthBearerProvider.cs │ └── SignalRUserIdProvider.cs ├── Filters │ ├── ApiExceptionFilterAttribute.cs │ └── ValidateModelAttribute.cs ├── Middleware │ ├── Processors │ │ ├── ErrorResponseProcessor.cs │ │ └── ResponseProcessor.cs │ └── RewindResponseMiddleware.cs ├── API │ └── v1 │ │ └── ApplicationInfoController.cs └── Web.Debug.config ├── Voyage.Database ├── Scripts │ ├── Seed │ │ ├── Chat.sql │ │ ├── UserPhone.sql │ │ ├── Role.sql │ │ ├── UserRole.sql │ │ └── ClientRole.sql │ └── Post-Deployment.sql ├── dbo │ ├── Tables │ │ ├── ChatChannel.sql │ │ ├── Role.sql │ │ ├── Notification.sql │ │ ├── ClientScopeType.sql │ │ ├── ProfileImage.sql │ │ ├── AuditLog.sql │ │ ├── ChatChannelMember.sql │ │ ├── ChatMessage.sql │ │ ├── ApplicationLog.sql │ │ ├── ActivityAudit.sql │ │ ├── RoleClaim.sql │ │ ├── LogMetadata.sql │ │ ├── ClientRole.sql │ │ ├── UserPhone.sql │ │ ├── UserLogin.sql │ │ ├── UserClaim.sql │ │ ├── AuditLogDetail.sql │ │ ├── ClientScope.sql │ │ ├── UserRole.sql │ │ ├── Client.sql │ │ └── User.sql │ └── Stored Procedures │ │ └── ActivityAuditInsert.sql └── local.publish.xml ├── readme_docs ├── images │ ├── Database_Diagram.PNG │ ├── DEVELOPMENT_implicit_auth1.png │ ├── DEVELOPMENT_implicit_auth2.png │ ├── DEVELOPMENT_implicit_auth3.png │ ├── DEVELOPMENT_implicit_auth4.png │ ├── DEVELOPMENT_implicit_auth5.png │ ├── DEVELOPMENT_postman_auth1.png │ ├── DEVELOPMENT_postman_auth2.png │ ├── DEVELOPMENT_postman_auth3.png │ ├── DEVELOPMENT_postman_auth4.png │ └── DEVELOPMENT_postman_auth5.png ├── STANDARDS-TESTING.md └── MIDDLEWARE.md ├── Voyage.Core ├── IIdentityProvider.cs ├── ForgotPasswordStep.cs ├── packages.config ├── Paths.cs ├── EventCodes.cs ├── Exceptions │ ├── NotFoundException.cs │ ├── UnauthorizedException.cs │ ├── PasswordRecoverException.cs │ ├── BadRequestException.cs │ └── ApiException.cs ├── EnumerableExtensions.cs ├── ExceptionExtensions.cs ├── Constants.cs └── Clients.cs ├── Voyage.Models ├── Enum │ ├── AccountType.cs │ ├── TransactionType.cs │ ├── PhoneType.cs │ └── MonitorType.cs ├── VerifyModel.cs ├── ApplicationInfoModel.cs ├── ChangeAccountStatusModel.cs ├── ISoftDeleteable.cs ├── CurrentUserModel.cs ├── UserApplicationSession.cs ├── ChatChannelModel.cs ├── Map │ ├── Profiles │ │ ├── NotificationProfile.cs │ │ ├── ActivityAuditProfile.cs │ │ ├── RoleProfile.cs │ │ ├── ChatProfile.cs │ │ └── ClaimProfile.cs │ ├── MappingConfig.cs │ └── AutoMapperModule.cs ├── ChatChannelMemberModel.cs ├── Entities │ ├── ApplicationRole.cs │ ├── ProfileImage.cs │ ├── ClientRole.cs │ ├── RoleClaim.cs │ ├── ChatChannel.cs │ ├── Notification.cs │ ├── ChatChannelMember.cs │ ├── UserPhone.cs │ ├── ApplicationLog.cs │ ├── ChatMessage.cs │ ├── ActivityAudit.cs │ ├── ApplicationUser.cs │ ├── ClientScopeType.cs │ └── ClientScope.cs ├── PhoneSecurityCodeModel.cs ├── ResponseErrorModel.cs ├── ChatMessageModel.cs ├── Validators │ ├── RoleModelValidator.cs │ ├── RuleBuilderExtensions.cs │ ├── PhoneSecurityCodeModelValidator.cs │ ├── ClaimModelValidator.cs │ ├── UserPhoneModelValidator.cs │ └── UserModelValidator.cs ├── ClientModel.cs ├── ClaimModel.cs ├── UserPhoneModel.cs ├── NotificationModel.cs ├── RoleModel.cs ├── ForgotPasswordModel.cs ├── ActivityAuditModel.cs ├── ProfileModel.cs ├── packages.config ├── RegistrationModel.cs ├── App.config └── UserModel.cs ├── Voyage.Services ├── Notification │ ├── Push │ │ ├── ChatHub.cs │ │ ├── NotificationHub.cs │ │ ├── IPushService.cs │ │ ├── SignalRContractResolver.cs │ │ └── PushService.cs │ └── INotificationService.cs ├── FileReader │ ├── IFileReaderService.cs │ └── FileReaderService.cs ├── ApplicationInfo │ ├── IApplicationInfoService.cs │ └── ApplicationInfoService.cs ├── Ant │ ├── IAntService.cs │ └── AntService.cs ├── Admin │ ├── IAdminService.cs │ └── AdminService.cs ├── IdentityManagers │ └── ApplicationRoleManager.cs ├── Identity │ └── IdentityResultResponse.cs ├── Audit │ └── IAuditService.cs ├── Profile │ └── IProfileService.cs ├── Chat │ └── IChatService.cs ├── Client │ └── IClientService.cs ├── PasswordRecovery │ └── IPasswordRecoverService.cs ├── Phone │ └── IPhoneService.cs ├── KeyContainer │ └── IRsaKeyContainerService.cs ├── Role │ └── IRoleService.cs ├── packages.config └── App.config ├── Voyage.Data ├── Repositories │ ├── Chat │ │ ├── IChatChannelRepository.cs │ │ ├── IChatMessageRepository.cs │ │ └── IChatChannelMemberRepository.cs │ ├── ClientRole │ │ └── IClientRoleRepository.cs │ ├── ClientScope │ │ └── IClientScopeRepository.cs │ ├── ProfileImage │ │ └── IProfileImageRepository.cs │ ├── ActivityAudit │ │ └── IActivityAuditRepository.cs │ ├── Notification │ │ └── INotificationRepository.cs │ ├── ClientScopeType │ │ └── IClientScopeTypeRepository.cs │ ├── ApplicationLog │ │ └── IApplicationLogRepository.cs │ ├── RoleClaim │ │ └── IRoleClaimRepository.cs │ ├── UserPhone │ │ └── IUserPhoneRepository.cs │ ├── Client │ │ └── IClientRepository.cs │ ├── IRepository.cs │ └── BaseRepository.cs ├── BaseAuditConfiguration.cs ├── packages.config ├── Stores │ └── CustomUserStore.cs ├── IVoyageDataContext.cs └── App.config ├── Voyage.Security.BasicToken ├── VoyageBasicTokenConfiguration.cs ├── packages.config ├── Voyage.Security.BasicToken.nuspec └── app.config ├── VoyageSpecFlow ├── Step_Definitions │ └── VoyageStepDefinitions │ │ ├── Visual Studio 2015 │ │ └── Visualizers │ │ │ └── attribcache140.bin │ │ ├── JwtSecurityTokenHandler.cs │ │ └── SpecFlowFeatureVoyageCalculatorStepDefinition.cs ├── Program.cs ├── Features │ └── VoyageStepfeatures │ │ ├── SpecFlowFeatureVoyageCalculator.feature │ │ ├── SpecFlowFeatureLogin.feature │ │ └── SpecFlowFeatureRoleBaseAuthentication.feature ├── packages.config └── App.config ├── Voyage.Api.UserManager ├── RoutePrefixConstants.cs ├── Voyage.Api.UserManager.nuspec ├── packages.config └── app.config ├── Voyage.Security.Oauth2 ├── Models │ └── LoginModel.cs ├── Views │ └── OAuth │ │ └── AuthorizeError.cshtml ├── Voyage.Security.Oauth2.nuspec ├── VoyageOauth2Configuration.cs ├── app.config ├── Content │ ├── ripples.css │ └── Site.css └── web.Debug.config ├── NOTICE ├── Voyage.Api.UnitTests └── Common │ ├── AutoMapperFixture │ ├── AutoMapperCollection.cs │ └── AutoMapperFixture.cs │ ├── ReflectionHelper.cs │ └── BaseUnitTest.cs ├── Voyage.Web.UnitTests ├── Common │ ├── AutoMapperFixture │ │ ├── AutoMapperCollection.cs │ │ └── AutoMapperFixture.cs │ ├── ReflectionHelper.cs │ └── BaseUnitTest.cs ├── ConstantsTests.cs └── Middleware │ └── TestEnvironmentMiddleware.cs ├── Voyage.Data.UnitTests ├── Common │ ├── AutoMapperFixture │ │ ├── AutoMapperCollection.cs │ │ └── AutoMapperFixture.cs │ ├── ReflectionHelper.cs │ └── BaseUnitTest.cs ├── packages.config ├── UserPhoneRepositoryTests.cs └── ActivityAuditRepositoryTests.cs ├── Voyage.Models.UnitTests ├── Common │ ├── AutoMapperFixture │ │ ├── AutoMapperCollection.cs │ │ └── AutoMapperFixture.cs │ ├── ReflectionHelper.cs │ └── BaseUnitTest.cs ├── ClaimModelTests.cs ├── RoleModelTests.cs ├── UserModelTests.cs ├── RegistrationModelTests.cs ├── AutoMapperValidationTests.cs ├── App.config ├── Validators │ ├── UserPhoneModelValidatorTests.cs │ ├── ClaimModelValidatorTests.cs │ └── RoleModelValidatorTests.cs └── Map │ └── Profiles │ └── RoleProfileTests.cs ├── Voyage.Services.UnitTests ├── Common │ ├── AutoMapperFixture │ │ ├── AutoMapperCollection.cs │ │ └── AutoMapperFixture.cs │ ├── ReflectionHelper.cs │ ├── TestDbAsyncEnumerator.cs │ ├── TestDbAsyncEnumerable.cs │ ├── BaseUnitTest.cs │ └── QueryableExtensions.cs ├── App.config └── ApplicationInfoServiceTests.cs ├── Voyage.Core.UnitTests ├── packages.config ├── ExceptionExtensionsTests.cs ├── BaseUnitTest.cs └── ArgumentExtensionsTests.cs └── deploy-package.bat /Voyage.Web/FILE.txt: -------------------------------------------------------------------------------- 1 | Voyage API v1.0 -------------------------------------------------------------------------------- /Voyage.Api/File.txt: -------------------------------------------------------------------------------- 1 | { "build": { "buildNumber": "1.0.0" } } -------------------------------------------------------------------------------- /Voyage.Web/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /Voyage.Api/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Voyage.Api.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Voyage.Web/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Voyage.Web.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Voyage.Database/Scripts/Seed/Chat.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM ChatChannelMember 2 | 3 | DELETE FROM ChatMessage 4 | 5 | DELETE FROM ChatChannel 6 | -------------------------------------------------------------------------------- /Voyage.Web/Content/Images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/Voyage.Web/Content/Images/favicon.ico -------------------------------------------------------------------------------- /readme_docs/images/Database_Diagram.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/Database_Diagram.PNG -------------------------------------------------------------------------------- /Voyage.Core/IIdentityProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Core 2 | { 3 | public interface IIdentityProvider 4 | { 5 | string GetUserName(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Voyage.Web/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/Voyage.Web/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /Voyage.Web/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/Voyage.Web/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Voyage.Web/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/Voyage.Web/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_implicit_auth1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_implicit_auth1.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_implicit_auth2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_implicit_auth2.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_implicit_auth3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_implicit_auth3.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_implicit_auth4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_implicit_auth4.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_implicit_auth5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_implicit_auth5.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_postman_auth1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_postman_auth1.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_postman_auth2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_postman_auth2.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_postman_auth3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_postman_auth3.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_postman_auth4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_postman_auth4.png -------------------------------------------------------------------------------- /readme_docs/images/DEVELOPMENT_postman_auth5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/readme_docs/images/DEVELOPMENT_postman_auth5.png -------------------------------------------------------------------------------- /Voyage.Api/Hubs/NotificationHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.SignalR; 2 | 3 | namespace Voyage.Api.Hubs 4 | { 5 | //public class NotificationHub : Hub 6 | //{ 7 | //} 8 | } -------------------------------------------------------------------------------- /Voyage.Models/Enum/AccountType.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Models.Enum 2 | { 3 | public enum AccountType 4 | { 5 | Checking = 0, 6 | Savings = 1 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Voyage.Models/Enum/TransactionType.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Models.Enum 2 | { 3 | public enum TransactionType 4 | { 5 | Withdrawal = 0, 6 | Deposit = 1 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Voyage.Services/Notification/Push/ChatHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.SignalR; 2 | 3 | namespace Voyage.Services.Notification.Push 4 | { 5 | public class ChatHub : Hub 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Voyage.Models/Enum/PhoneType.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Models.Enum 2 | { 3 | public enum PhoneType 4 | { 5 | Mobile, 6 | Office, 7 | Home, 8 | Other 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Services/FileReader/IFileReaderService.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Services.FileReader 2 | { 3 | public interface IFileReaderService 4 | { 5 | string ReadAllText(string filePath); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Voyage.Services/Notification/Push/NotificationHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.SignalR; 2 | 3 | namespace Voyage.Services.Notification.Push 4 | { 5 | public class NotificationHub : Hub 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/Chat/IChatChannelRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.Chat 2 | { 3 | public interface IChatChannelRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/Chat/IChatMessageRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.Chat 2 | { 3 | public interface IChatMessageRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/ClientRole/IClientRoleRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.ClientRole 2 | { 3 | public interface IClientRoleRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/ClientScope/IClientScopeRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.ClientScope 2 | { 3 | public interface IClientScopeRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Models/VerifyModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | 3 | namespace Voyage.Models 4 | { 5 | public class VerifyModel 6 | { 7 | [AntiXss] 8 | public string Code { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/ProfileImage/IProfileImageRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.ProfileImage 2 | { 3 | public interface IProfileImageRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/ActivityAudit/IActivityAuditRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.ActivityAudit 2 | { 3 | public interface IActivityAuditRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/Chat/IChatChannelMemberRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.Chat 2 | { 3 | public interface IChatChannelMemberRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/Notification/INotificationRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.Notification 2 | { 3 | public interface INotificationRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Models/Enum/MonitorType.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Models.Enum 2 | { 3 | public enum MonitorType 4 | { 5 | Database, 6 | HttpEndpoint, 7 | Error, 8 | Activity, 9 | Other 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Voyage.Core/ForgotPasswordStep.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Core 2 | { 3 | public enum ForgotPasswordStep 4 | { 5 | VerifyUser, 6 | VerifySecurityCode, 7 | VerifySecurityAnswers, 8 | ResetPassword 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/ClientScopeType/IClientScopeTypeRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Data.Repositories.ClientScopeType 2 | { 3 | public interface IClientScopeTypeRepository : IRepository 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Voyage.Models/ApplicationInfoModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | 3 | namespace Voyage.Models 4 | { 5 | public class ApplicationInfoModel 6 | { 7 | [AntiXss] 8 | public string BuildNumber { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Models/ChangeAccountStatusModel.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Models 2 | { 3 | public class ChangeAccountStatusModel 4 | { 5 | public bool? IsActive { get; set; } 6 | 7 | public bool? IsVerifyRequired { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Voyage.Security.BasicToken/VoyageBasicTokenConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace Voyage.Security.BasicToken 4 | { 5 | public class VoyageBasicTokenConfiguration 6 | { 7 | public IContainer Container { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /VoyageSpecFlow/Step_Definitions/VoyageStepDefinitions/Visual Studio 2015/Visualizers/attribcache140.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excellarateinc/voyage-api-dotnet/HEAD/VoyageSpecFlow/Step_Definitions/VoyageStepDefinitions/Visual Studio 2015/Visualizers/attribcache140.bin -------------------------------------------------------------------------------- /Voyage.Core/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Voyage.Models/ISoftDeleteable.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Models 2 | { 3 | /// 4 | /// Indicates if the model can be soft deleted 5 | /// 6 | public interface ISoftDeleteable 7 | { 8 | bool Deleted { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Services/ApplicationInfo/IApplicationInfoService.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Models; 2 | 3 | namespace Voyage.Services.ApplicationInfo 4 | { 5 | public interface IApplicationInfoService 6 | { 7 | ApplicationInfoModel GetApplicationInfo(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Voyage.Web/Models/LoginModel.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Core.Exceptions; 2 | 3 | namespace Voyage.Web.Models 4 | { 5 | public class LoginModel 6 | { 7 | public string ReturnUrl { get; set; } 8 | 9 | public NotFoundException NotFoundException { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Voyage.Api.UserManager/RoutePrefixConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Api.UserManager 2 | { 3 | public static class RoutePrefixConstants 4 | { 5 | public static class RoutePrefixes 6 | { 7 | public const string V1 = "api/v1"; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Api/Service References/Application Insights/ConnectedService.json: -------------------------------------------------------------------------------- 1 | { 2 | "ProviderId": "Microsoft.ApplicationInsights.ConnectedService.ConnectedServiceProvider", 3 | "Version": "7.18.214.2", 4 | "GettingStartedDocument": { 5 | "Uri": "https://go.microsoft.com/fwlink/?LinkID=613413" 6 | } 7 | } -------------------------------------------------------------------------------- /Voyage.Models/CurrentUserModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Voyage.Models 4 | { 5 | public class CurrentUserModel : UserModel 6 | { 7 | public IEnumerable Roles { get; set; } 8 | 9 | public string ProfileImage { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/Models/LoginModel.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Core.Exceptions; 2 | 3 | namespace Voyage.Security.Oauth2.Models 4 | { 5 | public class LoginModel 6 | { 7 | public string ReturnUrl { get; set; } 8 | 9 | public NotFoundException NotFoundException { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Voyage API 2 | 3 | Copyright 2017 Lighthouse Software, Inc. 4 | 5 | This product includes software developed at 6 | Lighthouse Software, Inc (http://www.LighthouseSoftware.com). 7 | 8 | This software contains code derived from Spring Framework 9 | and Spring Security developed at Pivotal (https://spring.io). 10 | -------------------------------------------------------------------------------- /Voyage.Services/Ant/IAntService.cs: -------------------------------------------------------------------------------- 1 | using AntPathMatching; 2 | using System.Collections.Generic; 3 | 4 | namespace Voyage.Services.Ant 5 | { 6 | public interface IAntService 7 | { 8 | IAnt GetAntPath(string pattern); 9 | 10 | IAnt[] GetAntPaths(IEnumerable patterns); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Voyage.Services/Admin/IAdminService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Voyage.Models; 3 | 4 | namespace Voyage.Services.Admin 5 | { 6 | public interface IAdminService 7 | { 8 | Task ToggleAccountStatus(string userId, ChangeAccountStatusModel changeAccountStatusModel); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Web/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Web 2 | { 3 | public static class Constants 4 | { 5 | public static string ApplicationName = "Voyage .Net Authorize"; 6 | 7 | public static class RoutePrefixes 8 | { 9 | public const string V1 = "api/v1"; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /VoyageSpecFlow/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VoyageSpecFlow 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Api/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace Voyage.Api 4 | { 5 | public class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Voyage.Services/FileReader/FileReaderService.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Voyage.Services.FileReader 4 | { 5 | public class FileReaderService : IFileReaderService 6 | { 7 | public string ReadAllText(string filePath) 8 | { 9 | return File.ReadAllText(filePath); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Voyage.Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using Owin; 2 | using Microsoft.Owin; 3 | 4 | [assembly: OwinStartup(typeof(Voyage.Web.Startup))] 5 | 6 | namespace Voyage.Web 7 | { 8 | public partial class Startup 9 | { 10 | public void Configuration(IAppBuilder app) 11 | { 12 | Configure(app); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Voyage.Models/UserApplicationSession.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | 3 | namespace Voyage.Web.Models 4 | { 5 | public class UserApplicationSession 6 | { 7 | [AntiXss] 8 | public string UserId { get; set; } 9 | 10 | [AntiXss] 11 | public string PasswordRecoveryToken { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Voyage.Web/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace Voyage.Web 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ChatChannel.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ChatChannel] 2 | ( 3 | [ChannelId] INT NOT NULL PRIMARY KEY IDENTITY (1, 1), 4 | [Name] NVARCHAR(100) NOT NULL, 5 | [CreatedBy] NVARCHAR(128) NOT NULL, 6 | [CreateDate] DATETIME2 NOT NULL DEFAULT GETDATE(), 7 | CONSTRAINT [FK_ChatChannel_User] FOREIGN KEY ([CreatedBy]) REFERENCES [User]([Id]) 8 | ) 9 | -------------------------------------------------------------------------------- /Voyage.Core/Paths.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Core 2 | { 3 | public static class Paths 4 | { 5 | public const string AuthorizePath = "/OAuth/Authorize"; 6 | public const string TokenPath = "/OAuth/Token"; 7 | public const string LoginPath = "/Account/Login"; 8 | public const string LogoutPath = "/Account/Logout"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/ApplicationLog/IApplicationLogRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Voyage.Data.Repositories.ApplicationLog 4 | { 5 | public interface IApplicationLogRepository : IRepository 6 | { 7 | IQueryable GetRecentActivity(int maxEvents = 10); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Voyage.Models/ChatChannelModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Voyage.Models 4 | { 5 | public class ChatChannelModel 6 | { 7 | public int ChannelId { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public string CreatedBy { get; set; } 12 | 13 | public DateTime CreateDate { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Models/Map/Profiles/NotificationProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Voyage.Models.Entities; 3 | 4 | namespace Voyage.Models.Map.Profiles 5 | { 6 | public class NotificationProfile : Profile 7 | { 8 | public NotificationProfile() 9 | { 10 | CreateMap().ReverseMap(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Voyage.Services/Notification/Push/IPushService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Voyage.Models; 3 | 4 | namespace Voyage.Services.Notification.Push 5 | { 6 | public interface IPushService 7 | { 8 | void PushNotification(NotificationModel model); 9 | 10 | void PushChatMessage(IList users, ChatMessageModel model); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Voyage.Api.UnitTests/Common/AutoMapperFixture/AutoMapperCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Voyage.Api.UnitTests.Common.AutoMapperFixture 4 | { 5 | [CollectionDefinition(CollectionName)] 6 | public class AutoMapperCollection : ICollectionFixture 7 | { 8 | public const string CollectionName = "AutoMapper Collection"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Models/ChatChannelMemberModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Voyage.Models 4 | { 5 | public class ChatChannelMemberModel 6 | { 7 | public int ChannelMemberId { get; set; } 8 | 9 | public int ChannelId { get; set; } 10 | 11 | public string UserId { get; set; } 12 | 13 | public DateTime CreateDate { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Web.UnitTests/Common/AutoMapperFixture/AutoMapperCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Voyage.Web.UnitTests.Common.AutoMapperFixture 4 | { 5 | [CollectionDefinition(CollectionName)] 6 | public class AutoMapperCollection : ICollectionFixture 7 | { 8 | public const string CollectionName = "AutoMapper Collection"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/Common/AutoMapperFixture/AutoMapperCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Voyage.Data.UnitTests.Common.AutoMapperFixture 4 | { 5 | [CollectionDefinition(CollectionName)] 6 | public class AutoMapperCollection : ICollectionFixture 7 | { 8 | public const string CollectionName = "AutoMapper Collection"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Common/AutoMapperFixture/AutoMapperCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Voyage.Models.UnitTests.Common.AutoMapperFixture 4 | { 5 | [CollectionDefinition(CollectionName)] 6 | public class AutoMapperCollection : ICollectionFixture 7 | { 8 | public const string CollectionName = "AutoMapper Collection"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ApplicationRole.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.AspNet.Identity.EntityFramework; 3 | 4 | namespace Voyage.Models.Entities 5 | { 6 | public class ApplicationRole : IdentityRole 7 | { 8 | public string Description { get; set; } 9 | 10 | public virtual ICollection Claims { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Voyage.Web/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace Voyage.Web.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | // Redirect the default landing page to the API helper page. 8 | [AllowAnonymous] 9 | public ActionResult Index() 10 | { 11 | return View(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Voyage.Api/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Api 2 | { 3 | /// 4 | /// API Constants 5 | /// 6 | public static class Constants 7 | { 8 | public static string ApplicationName = "Voyage .Net API"; 9 | 10 | public static class RoutePrefixes 11 | { 12 | public const string V1 = "api/v1"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/Role.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[Role] ( 2 | [Id] NVARCHAR (128) NOT NULL, 3 | [Name] NVARCHAR (256) NOT NULL, 4 | [Description] NVARCHAR(256) NOT NULL DEFAULT '', 5 | CONSTRAINT [PK_dbo.Roles] PRIMARY KEY CLUSTERED ([Id] ASC) 6 | ); 7 | 8 | 9 | GO 10 | CREATE UNIQUE NONCLUSTERED INDEX [RoleNameIndex] 11 | ON [dbo].[Role]([Name] ASC); 12 | 13 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/AutoMapperFixture/AutoMapperCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Voyage.Services.UnitTests.Common.AutoMapperFixture 4 | { 5 | [CollectionDefinition(CollectionName)] 6 | public class AutoMapperCollection : ICollectionFixture 7 | { 8 | public const string CollectionName = "AutoMapper Collection"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/Notification.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[Notification] 2 | ( 3 | [Id] INT NOT NULL PRIMARY KEY IDENTITY (1, 1), 4 | [Subject] NVARCHAR(128) NOT NULL, 5 | [Description] NVARCHAR(256) NOT NULL, 6 | [AssignedToUserId] NVARCHAR(128) NOT NULL, 7 | [IsRead] BIT NOT NULL DEFAULT 0, 8 | [CreatedBy] NVARCHAR(128) NOT NULL, 9 | [CreatedDate] DATETIME2 NOT NULL DEFAULT GETDATE() 10 | ) 11 | -------------------------------------------------------------------------------- /Voyage.Models/PhoneSecurityCodeModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using FluentValidation.Attributes; 3 | using System.Collections.Generic; 4 | using Voyage.Models.Validators; 5 | 6 | namespace Voyage.Models 7 | { 8 | [Validator(typeof(PhoneSecurityCodeModelValidator))] 9 | public class PhoneSecurityCodeModel 10 | { 11 | [AntiXss] 12 | public string Code { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Voyage.Models/Map/Profiles/ActivityAuditProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Voyage.Models.Entities; 3 | 4 | namespace Voyage.Models.Map.Profiles 5 | { 6 | public class ActivityAuditProfile : Profile 7 | { 8 | public ActivityAuditProfile() 9 | { 10 | CreateMap() 11 | .ForMember(_ => _.Id, opt => opt.Ignore()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Voyage.Services/IdentityManagers/ApplicationRoleManager.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Models.Entities; 2 | using Microsoft.AspNet.Identity; 3 | 4 | namespace Voyage.Services.IdentityManagers 5 | { 6 | public class ApplicationRoleManager : RoleManager 7 | { 8 | public ApplicationRoleManager(IRoleStore store) 9 | : base(store) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ClientScopeType.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ClientScopeType] 2 | ( 3 | [Id] nvarchar(128) NOT NULL PRIMARY KEY, 4 | [Name] NVARCHAR(500) NOT NULL, 5 | [Description] NVARCHAR(1000) NOT NULL, 6 | [CreatedBy] nvarchar(255) NOT NULL, 7 | [CreatedDate] datetime NOT NULL, 8 | [LastModifiedBy] nvarchar(255) NOT NULL, 9 | [LastModifiedDate] datetime NOT NULL, 10 | [IsDeleted] BIT NOT NULL 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /VoyageSpecFlow/Features/VoyageStepfeatures/SpecFlowFeatureVoyageCalculator.feature: -------------------------------------------------------------------------------- 1 | Feature: SpecFlowFeatureVoyage 2 | In order to avoid silly mistakes 3 | As a math idiot 4 | I want to be told the sum of two numbers 5 | 6 | @SmokeTest 7 | Scenario: Add two numbers 8 | Given I have entered 50 into the calculator 9 | And I have entered 70 into the calculator 10 | When I press add 11 | Then the result should be 120 on the screen 12 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ProfileImage.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ProfileImage] 2 | ( 3 | [ProfileImageId] INT NOT NULL PRIMARY KEY IDENTITY(1, 1), 4 | [UserId] NVARCHAR (128) NOT NULL, 5 | [ImageData] NVARCHAR(MAX) NOT NULL, 6 | CONSTRAINT [FK_ProfileImage_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User]([Id]) ON DELETE CASCADE 7 | ); 8 | 9 | GO 10 | CREATE NONCLUSTERED INDEX [IX_UserId] 11 | ON [dbo].[ProfileImage]([UserId] ASC); -------------------------------------------------------------------------------- /Voyage.Core/EventCodes.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Core 2 | { 3 | public static class EventCodes 4 | { 5 | public const string ActivityAudit = "Activity"; 6 | public const string Authorization = "Authorization"; 7 | public const string EntityValidation = "EntityValidation"; 8 | public const string RequireVerification = "401_verify_user"; 9 | public const string UserDisabled = "UserDisabled"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Voyage.Models/ResponseErrorModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using Newtonsoft.Json; 3 | 4 | namespace Voyage.Models 5 | { 6 | public class ResponseErrorModel 7 | { 8 | [AntiXss] 9 | public string Error { get; set; } 10 | 11 | [AntiXss] 12 | [JsonIgnore] 13 | public string Field { get; set; } 14 | 15 | [AntiXss] 16 | public string ErrorDescription { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/AuditLog.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[AuditLog] ( 2 | [AuditLogId] BIGINT IDENTITY (1, 1) NOT NULL, 3 | [UserName] NVARCHAR (MAX) NULL, 4 | [EventDateUTC] DATETIME NOT NULL, 5 | [EventType] INT NOT NULL, 6 | [TypeFullName] NVARCHAR (512) NOT NULL, 7 | [RecordId] NVARCHAR (256) NOT NULL, 8 | CONSTRAINT [PK_dbo.AuditLog] PRIMARY KEY CLUSTERED ([AuditLogId] ASC) 9 | ); 10 | 11 | -------------------------------------------------------------------------------- /Voyage.Models/Map/MappingConfig.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace Voyage.Models.Map 4 | { 5 | public static class MappingConfig 6 | { 7 | public static IMapper ConfigureMapper() 8 | { 9 | var config = new MapperConfiguration(c => 10 | { 11 | c.AddProfiles(typeof(MappingConfig).Assembly); 12 | }); 13 | 14 | return config.CreateMapper(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Services/Identity/IdentityResultResponse.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.Identity; 2 | 3 | namespace Voyage.Services.Identity 4 | { 5 | public class IdentityResultResponse 6 | { 7 | public IdentityResult IdentityResult { get; } 8 | 9 | public string Id { get; set; } 10 | 11 | public IdentityResultResponse(IdentityResult identityResult) 12 | { 13 | IdentityResult = identityResult; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Voyage.Models/ChatMessageModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Voyage.Models 4 | { 5 | public class ChatMessageModel 6 | { 7 | public int MessageId { get; set; } 8 | 9 | public int ChannelId { get; set; } 10 | 11 | public string Message { get; set; } 12 | 13 | public string Username { get; set; } 14 | 15 | public string CreatedBy { get; set; } 16 | 17 | public DateTime CreateDate { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Web/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | @{ 3 | Layout = null; 4 | } 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 |
14 |

Error.

15 |

An error occurred while processing your request.

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /Voyage.Models/Validators/RoleModelValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Voyage.Core; 3 | 4 | namespace Voyage.Models.Validators 5 | { 6 | public class RoleModelValidator : AbstractValidator 7 | { 8 | public RoleModelValidator() 9 | { 10 | RuleFor(_ => _.Name) 11 | .NotEmpty() 12 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "Name is a required field"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Services/Audit/IAuditService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Voyage.Models; 5 | using Voyage.Models.Entities; 6 | 7 | namespace Voyage.Services.Audit 8 | { 9 | public interface IAuditService 10 | { 11 | Task RecordAsync(ActivityAuditModel model); 12 | 13 | Task> GetAuditActivityWithinTimeAsync(string userName, string path, int timeInMinutes); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Core/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Voyage.Core.Exceptions 4 | { 5 | public class NotFoundException : ApiException 6 | { 7 | public NotFoundException() 8 | : base(HttpStatusCode.NotFound) 9 | { 10 | } 11 | 12 | public NotFoundException(string message) 13 | : base(HttpStatusCode.NotFound, Constants.ErrorCodes.EntityNotFound, message) 14 | { 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/RoleClaim/IRoleClaimRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | 4 | namespace Voyage.Data.Repositories.RoleClaim 5 | { 6 | public interface IRoleClaimRepository : IRepository 7 | { 8 | IQueryable GetClaimsByRole(string roleName); 9 | 10 | Task GetByRoleAndClaimAsync(string roleName, string claimType, string claimValue); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ChatChannelMember.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ChatChannelMember] 2 | ( 3 | [ChannelMemberId] INT NOT NULL PRIMARY KEY IDENTITY (1, 1), 4 | [ChannelId] INT NOT NULL, 5 | [UserId] NVARCHAR(128) NOT NULL, 6 | [CreateDate] DATETIME2 NOT NULL DEFAULT GETDATE(), 7 | CONSTRAINT [FK_ChatChannelMember_Channel] FOREIGN KEY ([ChannelId]) REFERENCES [ChatChannel]([ChannelId]), 8 | CONSTRAINT [FK_ChatChannelMember_User] FOREIGN KEY ([UserId]) REFERENCES [User]([Id]) 9 | ) 10 | -------------------------------------------------------------------------------- /Voyage.Models/Map/Profiles/RoleProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Voyage.Models.Entities; 3 | 4 | namespace Voyage.Models.Map.Profiles 5 | { 6 | public class RoleProfile : Profile 7 | { 8 | public RoleProfile() 9 | { 10 | CreateMap() 11 | .ForMember(_ => _.Name, opt => opt.MapFrom(src => src.Name)) 12 | .ForMember(_ => _.Id, opt => opt.MapFrom(src => src.Id)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Models/Validators/RuleBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Voyage.Models.Validators 4 | { 5 | public static class RuleBuilderExtensions 6 | { 7 | public static IRuleBuilderOptions WithErrorCodeMessage(this IRuleBuilderOptions options, string code, string message) 8 | { 9 | options.WithMessage("{0}::{1}", code, message); 10 | return options; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Voyage.Api/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using Owin; 3 | 4 | [assembly: OwinStartup(typeof(Voyage.Api.Startup))] 5 | namespace Voyage.Api 6 | { 7 | /// 8 | /// Application Startup. 9 | /// 10 | public partial class Startup 11 | { 12 | /// 13 | /// Configures the middleware. 14 | /// 15 | public void Configuration(IAppBuilder app) 16 | { 17 | Configure(app); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Voyage.Core/Exceptions/UnauthorizedException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Voyage.Core.Exceptions 4 | { 5 | public class UnauthorizedException : ApiException 6 | { 7 | public UnauthorizedException() 8 | : base(HttpStatusCode.Unauthorized) 9 | { 10 | } 11 | 12 | public UnauthorizedException(string message) 13 | : base(HttpStatusCode.Unauthorized, Constants.ErrorCodes.Unauthorized, message) 14 | { 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Services/Ant/AntService.cs: -------------------------------------------------------------------------------- 1 | using AntPathMatching; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Voyage.Services.Ant 6 | { 7 | public class AntService : IAntService 8 | { 9 | public IAnt GetAntPath(string pattern) => new AntPathMatching.Ant(pattern); 10 | 11 | public IAnt[] GetAntPaths(IEnumerable patterns) 12 | { 13 | return patterns?.Select(x => new AntPathMatching.Ant(x)).ToArray(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Voyage.Api/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace Voyage.Api 5 | { 6 | /// 7 | /// Route configuration. 8 | /// 9 | public class RouteConfig 10 | { 11 | /// 12 | /// Route registration. 13 | /// 14 | public static void RegisterRoutes(RouteCollection routes) 15 | { 16 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Voyage.Api/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Web.Mvc; 3 | using System.Web.Routing; 4 | 5 | namespace Voyage.Api 6 | { 7 | public class WebApiApplication : System.Web.HttpApplication 8 | { 9 | protected void Application_Start() 10 | { 11 | GlobalConfiguration.Configure(WebApiConfig.Register); 12 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 13 | RouteConfig.RegisterRoutes(RouteTable.Routes); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Voyage.Core/Exceptions/PasswordRecoverException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Voyage.Core.Exceptions 4 | { 5 | public class PasswordRecoverException : ApiException 6 | { 7 | public PasswordRecoverException(ForgotPasswordStep forgotPasswordStep, string message) 8 | : base(HttpStatusCode.BadRequest, message) 9 | { 10 | ForgotPasswordStep = forgotPasswordStep; 11 | } 12 | 13 | public ForgotPasswordStep ForgotPasswordStep { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Core/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Voyage.Core 7 | { 8 | public static class EnumerableExtensions 9 | { 10 | public static async Task> SelectManyAsync(this IEnumerable enumeration, Func>> func) 11 | { 12 | return (await Task.WhenAll(enumeration.Select(func))).SelectMany(s => s); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Services/Profile/IProfileService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Voyage.Models; 3 | 4 | namespace Voyage.Services.Profile 5 | { 6 | public interface IProfileService 7 | { 8 | Task GetCurrentUserAync(string userId); 9 | 10 | Task UpdateProfileAsync(string userId, ProfileModel model); 11 | 12 | Task GetProfileImage(string userId); 13 | 14 | Task GetInitialProfileImageAsync(string userId, string emailAddress); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Voyage.Web/Extensions/ModelStateExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.Identity; 2 | using System.Web.Http.ModelBinding; 3 | 4 | namespace Voyage.Web.Extensions 5 | { 6 | public static class ModelStateExtensions 7 | { 8 | public static void AddErrors(this ModelStateDictionary state, IdentityResult identityResult) 9 | { 10 | foreach (var error in identityResult.Errors) 11 | { 12 | state.AddModelError(string.Empty, error); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Voyage.Data/Repositories/UserPhone/IUserPhoneRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Voyage.Data.Repositories.UserPhone 4 | { 5 | public interface IUserPhoneRepository : IRepository 6 | { 7 | bool IsValidPhoneNumber(string phoneNumber, out string formatedPhoneNumber); 8 | 9 | string GetE164Format(string phoneNumber); 10 | 11 | Task SendSecurityCode(string phoneNumber, string securityCode); 12 | 13 | int Delete(object id); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Models/Map/AutoMapperModule.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace Voyage.Models.Map 4 | { 5 | public class AutoMapperModule : Module 6 | { 7 | protected override void Load(ContainerBuilder builder) 8 | { 9 | var instance = MappingConfig.ConfigureMapper(); 10 | 11 | // The mapper can be shared for the lifetime of the application, register it as a singleton 12 | builder.RegisterInstance(instance) 13 | .SingleInstance(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Voyage.Models/Validators/PhoneSecurityCodeModelValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Voyage.Core; 3 | 4 | namespace Voyage.Models.Validators 5 | { 6 | public class PhoneSecurityCodeModelValidator : AbstractValidator 7 | { 8 | public PhoneSecurityCodeModelValidator() 9 | { 10 | RuleFor(_ => _.Code) 11 | .Length(6) 12 | .WithErrorCodeMessage(Constants.ErrorCodes.InvalidLength, "Code is invlaid length"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Voyage.Api.UnitTests/Common/AutoMapperFixture/AutoMapperFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | using Voyage.Models.Map; 4 | 5 | namespace Voyage.Api.UnitTests.Common.AutoMapperFixture 6 | { 7 | public class AutoMapperFixture 8 | { 9 | private readonly Lazy _mapper = new Lazy(() => 10 | { 11 | var instance = MappingConfig.ConfigureMapper(); 12 | return instance; 13 | }); 14 | 15 | public IMapper MapperInstance => _mapper.Value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Models/ClientModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Voyage.Models 8 | { 9 | public class ClientModel 10 | { 11 | public string Id { get; set; } 12 | 13 | public string Identifier { get; set; } 14 | 15 | public string Secret { get; set; } 16 | 17 | public string RedirectUrl { get; set; } 18 | 19 | public List AllowedScopes { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Voyage.Web.UnitTests/Common/AutoMapperFixture/AutoMapperFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | using Voyage.Models.Map; 4 | 5 | namespace Voyage.Web.UnitTests.Common.AutoMapperFixture 6 | { 7 | public class AutoMapperFixture 8 | { 9 | private readonly Lazy _mapper = new Lazy(() => 10 | { 11 | var instance = MappingConfig.ConfigureMapper(); 12 | return instance; 13 | }); 14 | 15 | public IMapper MapperInstance => _mapper.Value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/Common/AutoMapperFixture/AutoMapperFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | using Voyage.Models.Map; 4 | 5 | namespace Voyage.Data.UnitTests.Common.AutoMapperFixture 6 | { 7 | public class AutoMapperFixture 8 | { 9 | private readonly Lazy _mapper = new Lazy(() => 10 | { 11 | var instance = MappingConfig.ConfigureMapper(); 12 | return instance; 13 | }); 14 | 15 | public IMapper MapperInstance => _mapper.Value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Common/AutoMapperFixture/AutoMapperFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | using Voyage.Models.Map; 4 | 5 | namespace Voyage.Models.UnitTests.Common.AutoMapperFixture 6 | { 7 | public class AutoMapperFixture 8 | { 9 | private readonly Lazy _mapper = new Lazy(() => 10 | { 11 | var instance = MappingConfig.ConfigureMapper(); 12 | return instance; 13 | }); 14 | 15 | public IMapper MapperInstance => _mapper.Value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ProfileImage.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace Voyage.Models.Entities 5 | { 6 | [Table("ProfileImage")] 7 | public class ProfileImage 8 | { 9 | [Key] 10 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 11 | public int ProfileImageId { get; set; } 12 | 13 | [Required] 14 | public string UserId { get; set; } 15 | 16 | public string ImageData { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/AutoMapperFixture/AutoMapperFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | using Voyage.Models.Map; 4 | 5 | namespace Voyage.Services.UnitTests.Common.AutoMapperFixture 6 | { 7 | public class AutoMapperFixture 8 | { 9 | private readonly Lazy _mapper = new Lazy(() => 10 | { 11 | var instance = MappingConfig.ConfigureMapper(); 12 | return instance; 13 | }); 14 | 15 | public IMapper MapperInstance => _mapper.Value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ChatMessage.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ChatMessage] 2 | ( 3 | [MessageId] INT NOT NULL PRIMARY KEY IDENTITY (1, 1), 4 | [ChannelId] INT NOT NULL, 5 | [Message] NVARCHAR(1000) NOT NULL, 6 | [Username] NVARCHAR(MAX) NOT NULL, 7 | [CreatedBy] NVARCHAR(128) NOT NULL, 8 | [CreateDate] DATETIME2 NOT NULL DEFAULT GETDATE(), 9 | CONSTRAINT [FK_ChatMessage_ChatChannel] FOREIGN KEY ([ChannelId]) REFERENCES [ChatChannel]([ChannelId]), 10 | CONSTRAINT [FK_ChatMessage_User] FOREIGN KEY ([CreatedBy]) REFERENCES [User]([Id]) 11 | ) 12 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/ClaimModelTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FluentValidation.Attributes; 3 | using Voyage.Models.Validators; 4 | using Xunit; 5 | 6 | namespace Voyage.Models.UnitTests 7 | { 8 | public class ClaimModelTests 9 | { 10 | [Fact] 11 | public void Class_Should_Have_Validator_Class() 12 | { 13 | typeof(ClaimModel) 14 | .Should() 15 | .BeDecoratedWith(_ => _.ValidatorType == typeof(ClaimModelValidator)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Voyage.Api/Extensions/ModelStateExtensions.cs: -------------------------------------------------------------------------------- 1 | // using Microsoft.AspNet.Identity; 2 | // using System.Web.Http.ModelBinding; 3 | 4 | // namespace Voyage.Api.Extensions 5 | // { 6 | // public static class ModelStateExtensions 7 | // { 8 | // public static void AddErrors(this ModelStateDictionary state, IdentityResult identityResult) 9 | // { 10 | // foreach (var error in identityResult.Errors) 11 | // { 12 | // state.AddModelError(string.Empty, error); 13 | // } 14 | // } 15 | // } 16 | // } -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ApplicationLog.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ApplicationLog] ( 2 | [Id] INT IDENTITY (1, 1) NOT NULL, 3 | [Message] NVARCHAR (MAX) NULL, 4 | [MessageTemplate] NVARCHAR (MAX) NULL, 5 | [Level] NVARCHAR (128) NULL, 6 | [TimeStamp] DATETIME NOT NULL, 7 | [Exception] NVARCHAR (MAX) NULL, 8 | [LogEvent] NVARCHAR (MAX) NULL, 9 | [Properties] XML NULL, 10 | CONSTRAINT [PK_dbo.VoyageLogs] PRIMARY KEY CLUSTERED ([Id] ASC) 11 | ); 12 | 13 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ActivityAudit.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ActivityAudit] ( 2 | [Id] INT IDENTITY (1, 1) NOT NULL, 3 | [RequestId] NVARCHAR (64) NULL, 4 | [Method] NVARCHAR (32) NULL, 5 | [Path] NVARCHAR (128) NULL, 6 | [IpAddress] NVARCHAR (64) NULL, 7 | [Date] DATETIME NOT NULL, 8 | [StatusCode] INT NOT NULL, 9 | [Error] NVARCHAR (MAX) NULL, 10 | [UserName] NVARCHAR (50) NULL, 11 | CONSTRAINT [PK_dbo.ActivityAudit] PRIMARY KEY CLUSTERED ([Id] ASC) 12 | ); 13 | 14 | -------------------------------------------------------------------------------- /VoyageSpecFlow/Step_Definitions/VoyageStepDefinitions/JwtSecurityTokenHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IdentityModel.Tokens.Jwt; 3 | 4 | namespace VoyageSpecFlow.Step_Definitions.VoyageStepDefinitions 5 | { 6 | internal class JwtSecurityTokenHandler 7 | { 8 | public JwtSecurityTokenHandler() 9 | { 10 | } 11 | 12 | public bool RequireExpirationTime { get; set; } 13 | 14 | internal JwtSecurityToken ReadToken(string jwtToken) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/RoleClaim.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[RoleClaim] ( 2 | [Id] INT IDENTITY (1, 1) NOT NULL, 3 | [RoleId] NVARCHAR (128) NOT NULL, 4 | [ClaimType] NVARCHAR (MAX) NULL, 5 | [ClaimValue] NVARCHAR (MAX) NULL, 6 | CONSTRAINT [PK_dbo.RoleClaims] PRIMARY KEY CLUSTERED ([Id] ASC), 7 | CONSTRAINT [FK_dbo.RoleClaims_dbo.Roles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id]) ON DELETE CASCADE 8 | ); 9 | 10 | 11 | GO 12 | CREATE NONCLUSTERED INDEX [IX_RoleId] 13 | ON [dbo].[RoleClaim]([RoleId] ASC); 14 | 15 | -------------------------------------------------------------------------------- /Voyage.Web/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace Voyage.Web 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Voyage.Web/AuthProviders/IdentityProvider.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Core; 2 | using Voyage.Web.Extensions; 3 | using Microsoft.Owin; 4 | 5 | namespace Voyage.Web.AuthProviders 6 | { 7 | public class IdentityProvider : IIdentityProvider 8 | { 9 | private readonly IOwinContext _context; 10 | 11 | public IdentityProvider(IOwinContext context) 12 | { 13 | _context = context.ThrowIfNull(nameof(context)); 14 | } 15 | 16 | public string GetUserName() 17 | { 18 | return _context.GetIdentityName(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Voyage.Services/Chat/IChatService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Voyage.Models; 4 | 5 | namespace Voyage.Services.Chat 6 | { 7 | public interface IChatService 8 | { 9 | Task> GetChannels(string userId); 10 | 11 | Task CreateChannel(string userId); 12 | 13 | Task> GetMessagesByChannelAsync(string userId, int channelId); 14 | 15 | Task CreateMessage(string userId, ChatMessageModel model); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Services/Notification/INotificationService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Voyage.Services.Notification 5 | { 6 | public interface INotificationService 7 | { 8 | Task> GetNotifications(string userId); 9 | 10 | Task CreateNotification(Models.NotificationModel notification); 11 | 12 | Task MarkNotificationAsRead(string userId, int notificationId); 13 | 14 | Task MarkAllNotificationsAsRead(string userId); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Voyage.Api/AuthProviders/IdentityProvider.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Core; 2 | using Microsoft.Owin; 3 | using Voyage.Api.Extensions; 4 | 5 | namespace Voyage.Api.AuthProviders 6 | { 7 | public class IdentityProvider : IIdentityProvider 8 | { 9 | private readonly IOwinContext _context; 10 | 11 | public IdentityProvider(IOwinContext context) 12 | { 13 | _context = context.ThrowIfNull(nameof(context)); 14 | } 15 | 16 | public string GetUserName() 17 | { 18 | return _context.GetIdentityName(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Voyage.Models/ClaimModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using FluentValidation.Attributes; 3 | using Newtonsoft.Json; 4 | using Voyage.Models.Validators; 5 | 6 | namespace Voyage.Models 7 | { 8 | [Validator(typeof(ClaimModelValidator))] 9 | public class ClaimModel 10 | { 11 | [JsonProperty("PermissionType")] 12 | [AntiXss] 13 | public string ClaimType { get; set; } 14 | 15 | [JsonProperty("PermissionValue")] 16 | [AntiXss] 17 | public string ClaimValue { get; set; } 18 | 19 | public int Id { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/RoleModelTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FluentValidation.Attributes; 3 | using Voyage.Models.Validators; 4 | using Xunit; 5 | 6 | namespace Voyage.Models.UnitTests 7 | { 8 | [Trait("Category", "Model.Validation")] 9 | public class RoleModelTests 10 | { 11 | [Fact] 12 | public void Class_Should_Have_Validator_Class() 13 | { 14 | typeof(RoleModel) 15 | .Should() 16 | .BeDecoratedWith(_ => _.ValidatorType == typeof(RoleModelValidator)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/UserModelTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FluentValidation.Attributes; 3 | using Voyage.Models.Validators; 4 | using Xunit; 5 | 6 | namespace Voyage.Models.UnitTests 7 | { 8 | [Trait("Category", "Model.Validation")] 9 | public class UserModelTests 10 | { 11 | [Fact] 12 | public void Class_Should_Have_Validator_Class() 13 | { 14 | typeof(UserModel) 15 | .Should() 16 | .BeDecoratedWith(_ => _.ValidatorType == typeof(UserModelValidator)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Api.UnitTests/Common/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Voyage.Api.UnitTests.Common 6 | { 7 | public static class ReflectionHelper 8 | { 9 | public static MethodInfo GetMethod(Expression> expression) 10 | { 11 | var member = expression.Body as MethodCallExpression; 12 | 13 | if (member != null) 14 | return member.Method; 15 | 16 | throw new ArgumentException("Expression is not a method", "expression"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/Common/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Voyage.Data.UnitTests.Common 6 | { 7 | public static class ReflectionHelper 8 | { 9 | public static MethodInfo GetMethod(Expression> expression) 10 | { 11 | var member = expression.Body as MethodCallExpression; 12 | 13 | if (member != null) 14 | return member.Method; 15 | 16 | throw new ArgumentException("Expression is not a method", "expression"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/LogMetadata.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[LogMetadata] ( 2 | [Id] BIGINT IDENTITY (1, 1) NOT NULL, 3 | [AuditLogId] BIGINT NOT NULL, 4 | [Key] NVARCHAR (MAX) NULL, 5 | [Value] NVARCHAR (MAX) NULL, 6 | CONSTRAINT [PK_dbo.LogMetadata] PRIMARY KEY CLUSTERED ([Id] ASC), 7 | CONSTRAINT [FK_dbo.LogMetadata_dbo.AuditLog_AuditLogId] FOREIGN KEY ([AuditLogId]) REFERENCES [dbo].[AuditLog] ([AuditLogId]) ON DELETE CASCADE 8 | ); 9 | 10 | 11 | GO 12 | CREATE NONCLUSTERED INDEX [IX_AuditLogId] 13 | ON [dbo].[LogMetadata]([AuditLogId] ASC); 14 | 15 | -------------------------------------------------------------------------------- /Voyage.Web.UnitTests/Common/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Voyage.Web.UnitTests.Common 6 | { 7 | public static class ReflectionHelper 8 | { 9 | public static MethodInfo GetMethod(Expression> expression) 10 | { 11 | var member = expression.Body as MethodCallExpression; 12 | 13 | if (member != null) 14 | return member.Method; 15 | 16 | throw new ArgumentException("Expression is not a method", "expression"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Web/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewBag.Title = "Index"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 | 7 |
8 |
9 |
10 |
11 | voyage-logo 12 |
13 |
14 |

Authorize Server

15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /Voyage.Data/Repositories/Client/IClientRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Voyage.Data.Repositories.Client 4 | { 5 | public interface IClientRepository : IRepository 6 | { 7 | Task ValidateClientAsync(string clientIdentifier, string clientSecret); 8 | 9 | Task GetByNameAsync(string clientIdentifier); 10 | 11 | Task UpdateFailedLoginAttemptsAsync(string id, bool isIncrement); 12 | 13 | Task IsLockedOutAsync(string id); 14 | 15 | Task UnlockClientAsync(string id); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ClientRole.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ClientRole] 2 | ( 3 | [ClientId] nvarchar(128) NOT NULL, 4 | [RoleId] nvarchar(128) NOT NULL, 5 | [Id] NVARCHAR(128) NOT NULL, 6 | CONSTRAINT [PK_dbo.ClientRoles] PRIMARY KEY CLUSTERED ([Id]), 7 | CONSTRAINT [FK_dbo.ClientRoles_dbo.Roles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id]) ON DELETE CASCADE, 8 | CONSTRAINT [FK_dbo.ClientRoles_dbo.Clients_Id] FOREIGN KEY ([ClientId]) REFERENCES [dbo].[Client] ([Id]) 9 | ); 10 | 11 | GO 12 | CREATE NONCLUSTERED INDEX [IX_ClientRole_Client_Id] 13 | ON [dbo].[ClientRole]([ClientId] ASC); 14 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Common/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Voyage.Models.UnitTests.Common 6 | { 7 | public static class ReflectionHelper 8 | { 9 | public static MethodInfo GetMethod(Expression> expression) 10 | { 11 | var member = expression.Body as MethodCallExpression; 12 | 13 | if (member != null) 14 | return member.Method; 15 | 16 | throw new ArgumentException("Expression is not a method", "expression"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/RegistrationModelTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FluentValidation.Attributes; 3 | using Voyage.Models.Validators; 4 | using Xunit; 5 | 6 | namespace Voyage.Models.UnitTests 7 | { 8 | [Trait("Category", "Model.Validation")] 9 | public class RegistrationModelTests 10 | { 11 | [Fact] 12 | public void Class_Should_Have_Validator_Class() 13 | { 14 | typeof(RegistrationModel) 15 | .Should() 16 | .BeDecoratedWith(_ => _.ValidatorType == typeof(RegistrationModelValidator)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Voyage.Services.UnitTests.Common 6 | { 7 | public static class ReflectionHelper 8 | { 9 | public static MethodInfo GetMethod(Expression> expression) 10 | { 11 | var member = expression.Body as MethodCallExpression; 12 | 13 | if (member != null) 14 | return member.Method; 15 | 16 | throw new ArgumentException("Expression is not a method", "expression"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/UserPhone.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[UserPhone] ( 2 | [Id] INT IDENTITY (1, 1) NOT NULL, 3 | [UserId] NVARCHAR (128) NOT NULL, 4 | [PhoneNumber] NVARCHAR (15) NOT NULL, 5 | [PhoneType] INT NOT NULL, 6 | [VerificationCode] NVARCHAR(50) NULL, 7 | CONSTRAINT [PK_dbo.UserPhones] PRIMARY KEY CLUSTERED ([Id] ASC), 8 | CONSTRAINT [FK_dbo.UserPhones_dbo.Users_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE 9 | ); 10 | 11 | 12 | GO 13 | CREATE NONCLUSTERED INDEX [IX_UserId] 14 | ON [dbo].[UserPhone]([UserId] ASC); 15 | 16 | -------------------------------------------------------------------------------- /Voyage.Models/Map/Profiles/ChatProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Voyage.Models.Entities; 3 | 4 | namespace Voyage.Models.Map.Profiles 5 | { 6 | public class ChatProfile : Profile 7 | { 8 | public ChatProfile() 9 | { 10 | CreateMap(); 11 | 12 | CreateMap() 13 | .ReverseMap() 14 | .ForMember(_ => _.User, opt => opt.Ignore()) 15 | .ForMember(_ => _.Channel, opt => opt.Ignore()); 16 | 17 | CreateMap(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Voyage.Models/UserPhoneModel.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Attributes; 2 | using Voyage.Models.Validators; 3 | using Embarr.WebAPI.AntiXss; 4 | 5 | namespace Voyage.Models 6 | { 7 | [Validator(typeof(UserPhoneModelValidator))] 8 | public class UserPhoneModel 9 | { 10 | public int Id { get; set; } 11 | 12 | [AntiXss] 13 | public string UserId { get; set; } 14 | 15 | [AntiXss] 16 | public string PhoneNumber { get; set; } 17 | 18 | [AntiXss] 19 | public string PhoneType { get; set; } 20 | 21 | [AntiXss] 22 | public string VerificationCode { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Voyage.Models/Map/Profiles/ClaimProfile.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using AutoMapper; 3 | using Voyage.Models.Entities; 4 | 5 | namespace Voyage.Models.Map.Profiles 6 | { 7 | public class ClaimProfile : Profile 8 | { 9 | public ClaimProfile() 10 | { 11 | CreateMap(); 12 | 13 | CreateMap() 14 | .ForMember(_ => _.ClaimValue, opt => opt.MapFrom(src => src.Value)) 15 | .ForMember(_ => _.ClaimType, opt => opt.MapFrom(src => src.Type)) 16 | .ForMember(_ => _.Id, opt => opt.Ignore()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Core/Exceptions/BadRequestException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Voyage.Core.Exceptions 4 | { 5 | public class BadRequestException : ApiException 6 | { 7 | public BadRequestException() 8 | : base(HttpStatusCode.BadRequest) 9 | { 10 | } 11 | 12 | public BadRequestException(string message) 13 | : base(HttpStatusCode.BadRequest, message) 14 | { 15 | } 16 | 17 | public BadRequestException(string errorCode, string errorDescription) 18 | : base(HttpStatusCode.BadRequest, errorCode, errorDescription) 19 | { 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Stored Procedures/ActivityAuditInsert.sql: -------------------------------------------------------------------------------- 1 | CREATE PROCEDURE [dbo].[ActivityAuditInsert] 2 | @RequestId nvarchar(64), 3 | @Method nvarchar(32), 4 | @Path nvarchar(128), 5 | @IpAddress nvarchar(64), 6 | @Date datetime, 7 | @StatusCode int, 8 | @Error nvarchar(max), 9 | @UserName nvarchar(50) 10 | AS 11 | 12 | INSERT INTO [dbo].[ActivityAudit] 13 | ([RequestId] 14 | ,[Method] 15 | ,[Path] 16 | ,[IpAddress] 17 | ,[Date] 18 | ,[StatusCode] 19 | ,[Error] 20 | ,[UserName]) 21 | VALUES 22 | (@RequestId 23 | ,@Method 24 | ,@Path 25 | ,@IpAddress 26 | ,@Date 27 | ,@StatusCode 28 | ,@Error 29 | ,@UserName) 30 | 31 | RETURN 0 32 | GO -------------------------------------------------------------------------------- /Voyage.Models/Entities/ClientRole.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("ClientRole")] 8 | public class ClientRole 9 | { 10 | [AntiXss] 11 | [Key] 12 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 13 | public string Id { get; set; } 14 | 15 | [AntiXss] 16 | [Required] 17 | public string ClientId { get; set; } 18 | 19 | [AntiXss] 20 | [Required] 21 | public string RoleId { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Voyage.Models/NotificationModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Embarr.WebAPI.AntiXss; 3 | 4 | namespace Voyage.Models 5 | { 6 | public class NotificationModel 7 | { 8 | public int Id { get; set; } 9 | 10 | [AntiXss] 11 | public string Subject { get; set; } 12 | 13 | [AntiXss] 14 | public string Description { get; set; } 15 | 16 | [AntiXss] 17 | public string AssignedToUserId { get; set; } 18 | 19 | public bool IsRead { get; set; } 20 | 21 | [AntiXss] 22 | public string CreatedBy { get; set; } 23 | 24 | public DateTime CreatedDate { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Voyage.Api/AuthProviders/QueryStringOAuthBearerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Owin.Security.OAuth; 3 | 4 | namespace Voyage.Api.AuthProviders 5 | { 6 | public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider 7 | { 8 | public override Task RequestToken(OAuthRequestTokenContext context) 9 | { 10 | var value = context.Request.Query.Get("access_token"); 11 | 12 | if (!string.IsNullOrEmpty(value)) 13 | { 14 | context.Token = value; 15 | } 16 | 17 | return Task.FromResult(null); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/UserLogin.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[UserLogin] ( 2 | [LoginProvider] NVARCHAR (128) NOT NULL, 3 | [ProviderKey] NVARCHAR (128) NOT NULL, 4 | [UserId] NVARCHAR (128) NOT NULL, 5 | [ApplicationUser_Id] NVARCHAR (128) NULL, 6 | CONSTRAINT [PK_dbo.UserLogins] PRIMARY KEY CLUSTERED ([LoginProvider] ASC, [ProviderKey] ASC, [UserId] ASC), 7 | CONSTRAINT [FK_dbo.UserLogins_dbo.Users_ApplicationUser_Id] FOREIGN KEY ([ApplicationUser_Id]) REFERENCES [dbo].[User] ([Id]) 8 | ); 9 | 10 | 11 | GO 12 | CREATE NONCLUSTERED INDEX [IX_ApplicationUser_Id] 13 | ON [dbo].[UserLogin]([ApplicationUser_Id] ASC); 14 | 15 | -------------------------------------------------------------------------------- /Voyage.Models/RoleModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentValidation.Attributes; 3 | using Newtonsoft.Json; 4 | using Voyage.Models.Validators; 5 | using Embarr.WebAPI.AntiXss; 6 | 7 | namespace Voyage.Models 8 | { 9 | [Validator(typeof(RoleModelValidator))] 10 | public class RoleModel 11 | { 12 | [AntiXss] 13 | public string Id { get; set; } 14 | 15 | [AntiXss] 16 | public string Name { get; set; } 17 | 18 | [AntiXss] 19 | public string Description { get; set; } 20 | 21 | [JsonProperty("Permissions")] 22 | public IEnumerable Claims { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/RoleClaim.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace Voyage.Models.Entities 5 | { 6 | [Table("RoleClaim")] 7 | public class RoleClaim 8 | { 9 | [Key] 10 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 11 | public int Id { get; set; } 12 | 13 | [Required] 14 | [ForeignKey("Role")] 15 | public string RoleId { get; set; } 16 | 17 | public virtual ApplicationRole Role { get; set; } 18 | 19 | public string ClaimType { get; set; } 20 | 21 | public string ClaimValue { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /Voyage.Database/Scripts/Seed/UserPhone.sql: -------------------------------------------------------------------------------- 1 | WITH UserPhone_CTE 2 | AS ( 3 | SELECT * 4 | FROM ( 5 | VALUES 6 | (N'fb9f65d2-699c-4f08-a2e4-8e6c28190a84', N'5555555555', 2) 7 | ) AS PhoneSeed([UserId], [Phone], [PhoneType]) 8 | ) 9 | -- Reference Data for User Phone 10 | MERGE INTO dbo.[UserPhone] AS [Target] 11 | USING UserPhone_CTE AS [Source] 12 | ON [Target].[UserId] = [Source].[UserId] 13 | WHEN NOT MATCHED BY TARGET 14 | THEN 15 | INSERT ( 16 | [UserId], 17 | [PhoneNumber], 18 | [PhoneType] 19 | ) 20 | VALUES ( 21 | [Source].[UserId] 22 | , [Source].[Phone] 23 | , [Source].[PhoneType] 24 | ) 25 | OUTPUT $action, inserted.*, deleted.*; 26 | -------------------------------------------------------------------------------- /Voyage.Models/ForgotPasswordModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using System.Collections.Generic; 3 | using Voyage.Core; 4 | 5 | namespace Voyage.Models 6 | { 7 | public class ForgotPasswordModel 8 | { 9 | public bool HasError { get; set; } 10 | 11 | [AntiXss] 12 | public string ErrorMessage { get; set; } 13 | 14 | public ForgotPasswordStep ForgotPasswordStep { get; set; } 15 | 16 | [AntiXss] 17 | public List Questions { get; set; } 18 | 19 | [AntiXss] 20 | public string PasswordRecoveryToken { get; set; } 21 | 22 | [AntiXss] 23 | public string UserId { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/UserClaim.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[UserClaim] ( 2 | [Id] INT IDENTITY (1, 1) NOT NULL, 3 | [UserId] NVARCHAR (MAX) NULL, 4 | [ClaimType] NVARCHAR (MAX) NULL, 5 | [ClaimValue] NVARCHAR (MAX) NULL, 6 | [ApplicationUser_Id] NVARCHAR (128) NULL, 7 | CONSTRAINT [PK_dbo.UserClaims] PRIMARY KEY CLUSTERED ([Id] ASC), 8 | CONSTRAINT [FK_dbo.UserClaims_dbo.Users_ApplicationUser_Id] FOREIGN KEY ([ApplicationUser_Id]) REFERENCES [dbo].[User] ([Id]) 9 | ); 10 | 11 | 12 | GO 13 | CREATE NONCLUSTERED INDEX [IX_ApplicationUser_Id] 14 | ON [dbo].[UserClaim]([ApplicationUser_Id] ASC); 15 | 16 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/AuditLogDetail.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[AuditLogDetail] ( 2 | [Id] BIGINT IDENTITY (1, 1) NOT NULL, 3 | [PropertyName] NVARCHAR (256) NOT NULL, 4 | [OriginalValue] NVARCHAR (MAX) NULL, 5 | [NewValue] NVARCHAR (MAX) NULL, 6 | [AuditLogId] BIGINT NOT NULL, 7 | CONSTRAINT [PK_dbo.AuditLogDetail] PRIMARY KEY CLUSTERED ([Id] ASC), 8 | CONSTRAINT [FK_dbo.AuditLogDetail_dbo.AuditLog_AuditLogId] FOREIGN KEY ([AuditLogId]) REFERENCES [dbo].[AuditLog] ([AuditLogId]) ON DELETE CASCADE 9 | ); 10 | 11 | 12 | GO 13 | CREATE NONCLUSTERED INDEX [IX_AuditLogId] 14 | ON [dbo].[AuditLogDetail]([AuditLogId] ASC); 15 | 16 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ChatChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("ChatChannel")] 8 | public class ChatChannel 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int ChannelId { get; set; } 13 | 14 | public string Name { get; set; } 15 | 16 | [ForeignKey("User")] 17 | public string CreatedBy { get; set; } 18 | 19 | public DateTime CreateDate { get; set; } 20 | 21 | public virtual ApplicationUser User { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Voyage.Models/Validators/ClaimModelValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Voyage.Core; 3 | 4 | namespace Voyage.Models.Validators 5 | { 6 | public class ClaimModelValidator : AbstractValidator 7 | { 8 | public ClaimModelValidator() 9 | { 10 | RuleFor(_ => _.ClaimType) 11 | .NotEmpty() 12 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "Claim type is a required field"); 13 | 14 | RuleFor(_ => _.ClaimValue) 15 | .NotEmpty() 16 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "Claim value is a required field"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Services/Client/IClientService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Voyage.Models; 4 | 5 | namespace Voyage.Services.Client 6 | { 7 | public interface IClientService 8 | { 9 | Task IsValidClientAsync(string clientId, string clientSecret); 10 | 11 | Task GetClientAsync(string clientId); 12 | 13 | Task IsLockedOutAsync(string clientId); 14 | 15 | Task UpdateFailedLoginAttemptsAsync(string clientId, bool isIncrement = true); 16 | 17 | Task UnlockClientAsync(string clientId); 18 | 19 | Task> GetScopeListByClientId(string clientId); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Voyage.Api/Filters/ApiExceptionFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Web.Http.Filters; 3 | using Voyage.Api.Extensions; 4 | using Voyage.Core.Exceptions; 5 | 6 | namespace Voyage.Api.Filters 7 | { 8 | public class ApiExceptionFilterAttribute : ExceptionFilterAttribute 9 | { 10 | public override void OnException(HttpActionExecutedContext context) 11 | { 12 | var exception = context.Exception as ApiException; 13 | if (exception != null) 14 | { 15 | context.Response = context.Request.CreateResponse(exception.StatusCode, exception.Message.ToRequestErrorModel()); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Voyage.Web/Filters/ApiExceptionFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Web.Http.Filters; 3 | using Voyage.Core.Exceptions; 4 | using Voyage.Web.Extensions; 5 | 6 | namespace Voyage.Web.Filters 7 | { 8 | public class ApiExceptionFilterAttribute : ExceptionFilterAttribute 9 | { 10 | public override void OnException(HttpActionExecutedContext context) 11 | { 12 | var exception = context.Exception as ApiException; 13 | if (exception != null) 14 | { 15 | context.Response = context.Request.CreateResponse(exception.StatusCode, exception.Message.ToRequestErrorModel()); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Voyage.Database/Scripts/Seed/Role.sql: -------------------------------------------------------------------------------- 1 | WITH Role_CTE 2 | AS ( 3 | SELECT * 4 | FROM ( 5 | VALUES 6 | (N'1cd39193-1f83-4d44-ab45-68c85be2acc8', N'Administrator') 7 | ,(N'927fe4a9-4e27-4635-a208-eb5afc953294', N'Basic') 8 | ,(N'149DF98B-8B2D-4AE6-AD3B-7A3EF3C7CF0E', N'Support') 9 | ) AS RoleSeed([Id], [RoleName]) 10 | ) 11 | -- Reference Data for Role 12 | MERGE INTO dbo.[ROLE] AS [Target] 13 | USING Role_CTE AS [Source] 14 | ON [Target].[NAME] = [Source].[RoleName] 15 | WHEN NOT MATCHED BY TARGET 16 | THEN 17 | INSERT ( 18 | [Id] 19 | , [NAME] 20 | ) 21 | VALUES ( 22 | [Source].[Id] 23 | ,[Source].[RoleName] 24 | ) 25 | OUTPUT $action, inserted.*, deleted.*; 26 | -------------------------------------------------------------------------------- /Voyage.Database/local.publish.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True 5 | Voyage 6 | Voyage.Database.sql 7 | Data Source=(localdb)\MSSQLLocalDB;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True 8 | 1 9 | 10 | -------------------------------------------------------------------------------- /Voyage.Web/Views/OAuth/AuthorizeError.cshtml: -------------------------------------------------------------------------------- 1 | @using System 2 | @using System.Security.Claims 3 | @using System.Web 4 | @using Microsoft.Owin 5 | @{ 6 | IOwinContext owinContext = Context.GetOwinContext(); 7 | var error = owinContext.Get("oauth.Error"); 8 | var errorDescription = owinContext.Get("oauth.ErrorDescription"); 9 | var errorUri = owinContext.Get("oauth.ErrorUri"); 10 | } 11 | 12 | 13 | 14 | Authorize Error 15 | 16 | 17 |

Katana.Sandbox.WebServer

18 |

OAuth2 Authorize Error

19 |

Error: @error

20 |

@errorDescription

21 | 22 | -------------------------------------------------------------------------------- /Voyage.Core/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace Voyage.Core 5 | { 6 | public static class ExceptionExtensions 7 | { 8 | public static string FlattenMessages(this AggregateException exception) 9 | { 10 | const int maxDepth = 5; 11 | var depth = 0; 12 | 13 | var sb = new StringBuilder(); 14 | 15 | Exception ex = exception; 16 | 17 | while (ex != null && depth < maxDepth) 18 | { 19 | sb.AppendLine(ex.Message); 20 | ex = ex.InnerException; 21 | ++depth; 22 | } 23 | 24 | return sb.ToString(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/Views/OAuth/AuthorizeError.cshtml: -------------------------------------------------------------------------------- 1 | @using System 2 | @using System.Security.Claims 3 | @using System.Web 4 | @using Microsoft.Owin 5 | @{ 6 | IOwinContext owinContext = Context.GetOwinContext(); 7 | var error = owinContext.Get("oauth.Error"); 8 | var errorDescription = owinContext.Get("oauth.ErrorDescription"); 9 | var errorUri = owinContext.Get("oauth.ErrorUri"); 10 | } 11 | 12 | 13 | 14 | Authorize Error 15 | 16 | 17 |

Katana.Sandbox.WebServer

18 |

OAuth2 Authorize Error

19 |

Error: @error

20 |

@errorDescription

21 | 22 | -------------------------------------------------------------------------------- /Voyage.Services/PasswordRecovery/IPasswordRecoverService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Voyage.Models; 4 | using Voyage.Web.Models; 5 | 6 | namespace Voyage.Services.PasswordRecovery 7 | { 8 | public interface IPasswordRecoverService 9 | { 10 | Task ValidateUserInfoAsync(string userName, string phoneNumber); 11 | 12 | Task VerifyCodeAsync(UserApplicationSession appUser, string code); 13 | 14 | ForgotPasswordModel VerifySecurityAnswers(string userId, List anwers); 15 | 16 | Task ResetPasswordAsync(UserApplicationSession appUser, string newPassword, string confirmNewPassword); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/Notification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("Notification")] 8 | public class Notification 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int Id { get; set; } 13 | 14 | public string Subject { get; set; } 15 | 16 | public string Description { get; set; } 17 | 18 | public string AssignedToUserId { get; set; } 19 | 20 | public bool IsRead { get; set; } 21 | 22 | public string CreatedBy { get; set; } 23 | 24 | public DateTime CreatedDate { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /Voyage.Security.BasicToken/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Voyage.Services/Phone/IPhoneService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Voyage.Services.Phone 4 | { 5 | public interface IPhoneService 6 | { 7 | string GenerateSecurityCode(); 8 | 9 | Task InsertSecurityCodeAsync(int phoneId, string code); 10 | 11 | bool IsValidPhoneNumber(string phoneNumber, out string formatedPhoneNumber); 12 | 13 | string GetE164Format(string phoneNumber); 14 | 15 | Task SendSecurityCodeAsync(string phoneNumber, string securityCode); 16 | 17 | Task SendSecurityCodeToUserPhoneNumberAsync(string userName); 18 | 19 | Task IsValidSecurityCodeAsync(string userId, string securityCode); 20 | 21 | Task ClearUserPhoneSecurityCodeAsync(string userId); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Voyage.Api.UserManager/Voyage.Api.UserManager.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VoyageResourceServer 5 | 1.0.0 6 | Resource Server Plugin 7 | Voyage 8 | LSS Inc. 9 | http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE 10 | http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE 11 | http://ICON_URL_HERE_OR_DELETE_THIS_LINE 12 | false 13 | Plugin for Voyage to manage Users 14 | Plugin for Voyage to manage Users 15 | Copyright 2017 16 | 17 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/ClientScope.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ClientScope] 2 | ( 3 | [Id] nvarchar(128) PRIMARY KEY NOT NULL, 4 | [ClientId] nvarchar(128) NOT NULL, 5 | [ClientScopeTypeId] nvarchar(128) NOT NULL, 6 | [CreatedBy] nvarchar(255) NOT NULL, 7 | [CreatedDate] datetime NOT NULL, 8 | [LastModifiedBy] nvarchar(255) NOT NULL, 9 | [LastModifiedDate] datetime NOT NULL, 10 | [IsDeleted] BIT NOT NULL, 11 | CONSTRAINT [FK_dbo.ClientScopes_dbo.Clients_Id] FOREIGN KEY ([ClientId]) REFERENCES [dbo].[Client] ([Id]), 12 | CONSTRAINT [FK_dbo.ClientScopes_dbo.Client_Scope_Type_Id] FOREIGN KEY ([ClientScopeTypeId]) REFERENCES [dbo].[ClientScopeType] ([Id]) 13 | ); 14 | 15 | GO 16 | CREATE NONCLUSTERED INDEX [IX_ClientScope_Client_Id] 17 | ON [dbo].[ClientScope]([ClientId] ASC); -------------------------------------------------------------------------------- /Voyage.Models/ActivityAuditModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using System; 3 | 4 | namespace Voyage.Models 5 | { 6 | public class ActivityAuditModel 7 | { 8 | [AntiXss] 9 | public string RequestId { get; set; } 10 | 11 | [AntiXss] 12 | public string Method { get; set; } 13 | 14 | [AntiXss] 15 | public string Path { get; set; } 16 | 17 | [AntiXss] 18 | public string IpAddress { get; set; } 19 | 20 | [AntiXss] 21 | public DateTime Date { get; set; } 22 | 23 | [AntiXss] 24 | public int StatusCode { get; set; } 25 | 26 | [AntiXss] 27 | public string Error { get; set; } 28 | 29 | [AntiXss] 30 | public string UserName { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/AutoMapperValidationTests.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Models.UnitTests.Common; 2 | using Voyage.Models.UnitTests.Common.AutoMapperFixture; 3 | using Xunit; 4 | 5 | namespace Voyage.Models.UnitTests 6 | { 7 | [Collection(AutoMapperCollection.CollectionName)] 8 | public class AutoMapperValidationTests : BaseUnitTest 9 | { 10 | private readonly AutoMapperFixture _mappingFixture; 11 | 12 | public AutoMapperValidationTests(AutoMapperFixture mappingFixture) 13 | { 14 | _mappingFixture = mappingFixture; 15 | } 16 | 17 | [Fact] 18 | public void Configuration_Should_Be_Valid() 19 | { 20 | _mappingFixture.MapperInstance.ConfigurationProvider.AssertConfigurationIsValid(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Voyage.Web.UnitTests/ConstantsTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Voyage.Web.UnitTests.Common; 3 | using Xunit; 4 | 5 | namespace Voyage.Web.UnitTests 6 | { 7 | /// 8 | /// The route prefixes are critical to web api routing, let's test the constants so that 9 | /// if they accidently change a test breaks 10 | /// 11 | public class ConstantsTests : BaseUnitTest 12 | { 13 | [Fact] 14 | public void ApplicationName_Should_Return_Known_Value() 15 | { 16 | Constants.ApplicationName.Should().Be("Voyage .Net Authorize"); 17 | } 18 | 19 | [Fact] 20 | public void V1_Should_Return_Known_Value() 21 | { 22 | Constants.RoutePrefixes.V1.Should().Be("api/v1"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/UserRole.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[UserRole] ( 2 | [UserId] NVARCHAR (128) NOT NULL, 3 | [RoleId] NVARCHAR (128) NOT NULL, 4 | [ApplicationUser_Id] NVARCHAR (128) NULL, 5 | CONSTRAINT [PK_dbo.UserRoles] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC), 6 | CONSTRAINT [FK_dbo.UserRoles_dbo.Roles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id]) ON DELETE CASCADE, 7 | CONSTRAINT [FK_dbo.UserRoles_dbo.Users_ApplicationUser_Id] FOREIGN KEY ([ApplicationUser_Id]) REFERENCES [dbo].[User] ([Id]) 8 | ); 9 | 10 | 11 | GO 12 | CREATE NONCLUSTERED INDEX [IX_RoleId] 13 | ON [dbo].[UserRole]([RoleId] ASC); 14 | 15 | 16 | GO 17 | CREATE NONCLUSTERED INDEX [IX_ApplicationUser_Id] 18 | ON [dbo].[UserRole]([ApplicationUser_Id] ASC); 19 | 20 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ChatChannelMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("ChatChannelMember")] 8 | public class ChatChannelMember 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int ChannelMemberId { get; set; } 13 | 14 | [ForeignKey("Channel")] 15 | public int ChannelId { get; set; } 16 | 17 | [ForeignKey("User")] 18 | public string UserId { get; set; } 19 | 20 | public DateTime CreateDate { get; set; } 21 | 22 | public virtual ApplicationUser User { get; set; } 23 | 24 | public virtual ChatChannel Channel { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Voyage.Security.BasicToken/Voyage.Security.BasicToken.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VoyageSecurityBasicToken 5 | 1.0.3 6 | Security BasicToken 7 | Voyage 8 | Voyage Author 9 | http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE 10 | http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE 11 | http://ICON_URL_HERE_OR_DELETE_THIS_LINE 12 | false 13 | BasicToken Security 14 | BasicToken packaging. 15 | Copyright 2017 16 | Voyage Basic Token 17 | 18 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/Voyage.Security.Oauth2.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VoyageSecurityOAuth2 5 | 1.0.0 6 | Voyage Security 7 | Voyage 8 | lighthouse Software Solutions 9 | http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE 10 | http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE 11 | http://ICON_URL_HERE_OR_DELETE_THIS_LINE 12 | false 13 | Voyage OAuth2 Security 14 | Voyage Security for OAuth2 15 | Copyright 2017 16 | Voyage OAuth2 17 | 18 | -------------------------------------------------------------------------------- /VoyageSpecFlow/Features/VoyageStepfeatures/SpecFlowFeatureLogin.feature: -------------------------------------------------------------------------------- 1 | Feature: Voyage Login & Logout 2 | In order to get access to protected sections of the site 3 | A user 4 | Should be able to sign in and sing out 5 | 6 | Scenario: User is not signed up 7 | Given I do not exist as a user 8 | When I sign in with valid credentials 9 | Then I see an invalid login message 10 | And I should be signed out 11 | 12 | Scenario: User logged in successfully 13 | Given I exist as a user 14 | When I sign in with valid credentials 15 | Then I should be signed in 16 | And I should be on home page 17 | 18 | Scenario: User logged out 19 | Given I am logged in 20 | Then I should be on home page 21 | When I click sign out button 22 | Then I should be signed out -------------------------------------------------------------------------------- /Voyage.Services/KeyContainer/IRsaKeyContainerService.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Voyage.Services.KeyContainer 4 | { 5 | public interface IRsaKeyContainerService 6 | { 7 | /// 8 | /// Create Rsa provider object using a given key from key container. If key does not exist in key container new one will be created and use to create Rsa provider object. 9 | /// 10 | /// 11 | RSACryptoServiceProvider GetRsaCryptoServiceProviderFromKeyContainer(string keyName = "VoyageKey"); 12 | 13 | /// 14 | /// Delete key from key container 15 | /// 16 | /// 17 | void DeleteKeyFromContainer(string keyName = "VoyageKey"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Voyage.Web/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | 9 | namespace Voyage.Web 10 | { 11 | #pragma warning disable SA1649 // File name must match first type name 12 | public class MvcApplication : System.Web.HttpApplication 13 | #pragma warning restore SA1649 // File name must match first type name 14 | { 15 | protected void Application_Start() 16 | { 17 | AreaRegistration.RegisterAllAreas(); 18 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 19 | RouteConfig.RegisterRoutes(RouteTable.Routes); 20 | BundleConfig.RegisterBundles(BundleTable.Bundles); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Voyage.Core/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Voyage.Core 2 | { 3 | public static class Constants 4 | { 5 | public static class ErrorCodes 6 | { 7 | public const string MissingField = "missing.required.field"; 8 | public const string InvalidEmail = "invalid.email"; 9 | public const string InvalidLength = "invalid.length"; 10 | public const string InvalidDependentRule = "invalid.dependent.rule"; 11 | public const string EntityNotFound = "notfound.entity"; 12 | public const string Unauthorized = "error.unauthorized"; 13 | public const string Forbidden = "error.forbidden"; 14 | public const string InvalidPhoneNumber = "invalid.phonenumber"; 15 | public const string InvalidPassword = "invalid.password"; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Voyage.Api/AuthProviders/SignalRUserIdProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IdentityModel.Claims; 2 | using Microsoft.AspNet.SignalR; 3 | using Microsoft.Owin.Security; 4 | using Voyage.Core; 5 | 6 | namespace Voyage.Api.AuthProviders 7 | { 8 | public class SignalRUserIdProvider : IUserIdProvider 9 | { 10 | private readonly IAuthenticationManager _authenticationManager; 11 | 12 | public SignalRUserIdProvider(IAuthenticationManager authenticationManager) 13 | { 14 | _authenticationManager = authenticationManager.ThrowIfNull(nameof(authenticationManager)); 15 | } 16 | 17 | public string GetUserId(IRequest request) 18 | { 19 | var claim = _authenticationManager.User.FindFirst(_ => _.Type == ClaimTypes.NameIdentifier); 20 | return claim?.Value; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/Client.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[Client] 2 | ( 3 | [Id] nvarchar(128) PRIMARY KEY NOT NULL, 4 | [Name] nvarchar(500) NOT NULL, 5 | [ClientIdentifier] nvarchar(500) NOT NULL, 6 | [ClientSecret] nvarchar(1000) NOT NULL, 7 | [RedirectUri] nvarchar(500) NOT NULL, 8 | [IsSecretRequired] bit NOT NULL, 9 | [IsScoped] bit NOT NULL, 10 | [IsAutoApprove] bit NOT NULL, 11 | [AccessTokenValiditySeconds] int NOT NULL, 12 | [RefreshTokenValiditySeconds] int NOT NULL, 13 | [FailedLoginAttempts] int NULL, 14 | [ForceTokenExpiredate] DateTime NULL, 15 | [IsEnabled] bit NOT NULL, 16 | [IsAccountLocked] bit NOT NULL, 17 | [CreatedBy] nvarchar(255) NOT NULL, 18 | [CreatedDate] datetime NOT NULL, 19 | [LastModifiedBy] nvarchar(255) NOT NULL, 20 | [LastModifiedDate] datetime NOT NULL, 21 | [IsDeleted] BIT NOT NULL 22 | ) 23 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/UserPhone.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using Voyage.Models.Enum; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("UserPhone")] 8 | public class UserPhone 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int Id { get; set; } 13 | 14 | [Required] 15 | [ForeignKey("User")] 16 | public string UserId { get; set; } 17 | 18 | public virtual ApplicationUser User { get; set; } 19 | 20 | [MaxLength(15)] 21 | [Required] 22 | public string PhoneNumber { get; set; } 23 | 24 | [Required] 25 | public PhoneType PhoneType { get; set; } 26 | 27 | public string VerificationCode { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Voyage.Models/ProfileModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Embarr.WebAPI.AntiXss; 3 | using FluentValidation.Attributes; 4 | using Voyage.Models.Validators; 5 | 6 | namespace Voyage.Models 7 | { 8 | [Validator(typeof(ProfileModelValidator))] 9 | public class ProfileModel 10 | { 11 | [AntiXss] 12 | public string Email { get; set; } 13 | 14 | [AntiXss] 15 | public string FirstName { get; set; } 16 | 17 | [AntiXss] 18 | public string LastName { get; set; } 19 | 20 | [AntiXss] 21 | public string ProfileImage { get; set; } 22 | 23 | public List Phones { get; set; } 24 | 25 | [AntiXss] 26 | public string CurrentPassword { get; set; } 27 | 28 | [AntiXss] 29 | public string NewPassword { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | 4 | namespace Voyage.Data.Repositories 5 | { 6 | /// 7 | /// Basic repository 8 | /// 9 | public interface IRepository 10 | { 11 | Task SaveChangesAsync(); 12 | } 13 | 14 | /// 15 | /// Repository that returns generic type TModel 16 | /// 17 | /// Type of the model that the repository will return 18 | public interface IRepository : IRepository 19 | { 20 | Task AddAsync(TModel model); 21 | 22 | Task UpdateAsync(TModel model); 23 | 24 | IQueryable GetAll(); 25 | 26 | Task GetAsync(object id); 27 | 28 | Task DeleteAsync(object id); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Voyage.Models/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Voyage.Database/Scripts/Seed/UserRole.sql: -------------------------------------------------------------------------------- 1 | WITH UserRole_CTE 2 | AS ( 3 | SELECT * 4 | FROM ( 5 | VALUES 6 | (N'fb9f65d2-699c-4f08-a2e4-8e6c28190a84', N'1cd39193-1f83-4d44-ab45-68c85be2acc8') 7 | ,(N'fb9f65d2-699c-4f08-a2e4-8e6c28190a84', N'927fe4a9-4e27-4635-a208-eb5afc953294') 8 | ,(N'7E335050-E14D-4769-B2E1-E604CBEA69F2', N'149DF98B-8B2D-4AE6-AD3B-7A3EF3C7CF0E') 9 | ) AS UserRole([UserId], [RoleId]) 10 | ) 11 | -- Reference Data for Role 12 | MERGE INTO dbo.[UserRole] AS [Target] 13 | USING UserRole_CTE AS [Source] 14 | ON [Target].[UserId] = [Source].[UserId] and [Target].[RoleId] = [Source].[RoleId] 15 | WHEN NOT MATCHED BY TARGET 16 | THEN 17 | INSERT ( 18 | [UserId],[RoleId],[ApplicationUser_Id] 19 | ) 20 | VALUES ( 21 | [Source].[UserId] 22 | ,[Source].[RoleId] 23 | ,[Source].[UserId] 24 | ) 25 | OUTPUT $action, inserted.*, deleted.*; 26 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ApplicationLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("ApplicationLog")] 8 | public class ApplicationLog 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int Id { get; set; } 13 | 14 | public string Message { get; set; } 15 | 16 | public string MessageTemplate { get; set; } 17 | 18 | [MaxLength(128)] 19 | public string Level { get; set; } 20 | 21 | public DateTime TimeStamp { get; set; } 22 | 23 | public string Exception { get; set; } 24 | 25 | public string LogEvent { get; set; } 26 | 27 | [Column(TypeName = "xml")] 28 | public string Properties { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Voyage.Models/RegistrationModel.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using FluentValidation.Attributes; 3 | using System.Collections.Generic; 4 | using Voyage.Models.Validators; 5 | 6 | namespace Voyage.Models 7 | { 8 | [Validator(typeof(RegistrationModelValidator))] 9 | public class RegistrationModel 10 | { 11 | [AntiXss] 12 | public string UserName { get; set; } 13 | 14 | [AntiXss] 15 | public string Email { get; set; } 16 | 17 | [AntiXss] 18 | public string Password { get; set; } 19 | 20 | [AntiXss] 21 | public string ConfirmPassword { get; set; } 22 | 23 | [AntiXss] 24 | public string FirstName { get; set; } 25 | 26 | [AntiXss] 27 | public string LastName { get; set; } 28 | 29 | [AntiXss] 30 | public List Phones { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/VoyageOauth2Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | 4 | namespace Voyage.Security.Oauth2 5 | { 6 | public class VoyageOauth2Configuration 7 | { 8 | public IContainer Container { get; set; } 9 | 10 | /// 11 | /// Default value you will be set if not provided. Default value: /OAuth/Authorize 12 | /// 13 | public string AuthorizeEndpointPath { get; set; } 14 | 15 | /// 16 | /// Default value you will be set if not provided. Default value: /OAuth/Token 17 | /// 18 | public string TokenEndpointPath { get; set; } 19 | 20 | public TimeSpan AccessTokenExpireTimeSpan { get; set; } 21 | 22 | public bool AllowInsecureHttp { get; set; } 23 | 24 | public string TokenExpireSeconds { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /Voyage.Models/Entities/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("ChatMessage")] 8 | public class ChatMessage 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int MessageId { get; set; } 13 | 14 | [ForeignKey("Channel")] 15 | public int ChannelId { get; set; } 16 | 17 | public string Message { get; set; } 18 | 19 | public string Username { get; set; } 20 | 21 | [ForeignKey("User")] 22 | public string CreatedBy { get; set; } 23 | 24 | public DateTime CreateDate { get; set; } 25 | 26 | public virtual ApplicationUser User { get; set; } 27 | 28 | public virtual ChatChannel Channel { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Voyage.Api/Filters/ValidateModelAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Web.Http.Controllers; 4 | using System.Web.Http.Filters; 5 | using Voyage.Api.Extensions; 6 | 7 | namespace Voyage.Api.Filters 8 | { 9 | /// 10 | /// Returns a BadRequest response if the model is invalid 11 | /// 12 | public class ValidateModelAttribute : ActionFilterAttribute 13 | { 14 | public override void OnActionExecuting(HttpActionContext actionContext) 15 | { 16 | if (!actionContext.ModelState.IsValid) 17 | { 18 | actionContext.Response = actionContext 19 | .Request 20 | .CreateResponse( 21 | HttpStatusCode.BadRequest, 22 | actionContext.ModelState.ConvertToResponseModel()); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Voyage.Web/Filters/ValidateModelAttribute.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Web.Extensions; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Web.Http.Controllers; 5 | using System.Web.Http.Filters; 6 | 7 | namespace Voyage.Web.Filters 8 | { 9 | /// 10 | /// Returns a BadRequest response if the model is invalid 11 | /// 12 | public class ValidateModelAttribute : ActionFilterAttribute 13 | { 14 | public override void OnActionExecuting(HttpActionContext actionContext) 15 | { 16 | if (!actionContext.ModelState.IsValid) 17 | { 18 | actionContext.Response = actionContext 19 | .Request 20 | .CreateResponse( 21 | HttpStatusCode.BadRequest, 22 | actionContext.ModelState.ConvertToResponseModel()); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /VoyageSpecFlow/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Voyage.Data/BaseAuditConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Models.Entities; 2 | using Microsoft.AspNet.Identity.EntityFramework; 3 | using TrackerEnabledDbContext.Common.Configuration; 4 | 5 | namespace Voyage.Data 6 | { 7 | public static class BaseAuditConfiguration 8 | { 9 | public static void Configure() 10 | { 11 | EntityTracker.TrackAllProperties(); 12 | 13 | EntityTracker.TrackAllProperties() 14 | .Except(_ => _.PasswordHash); 15 | 16 | EntityTracker.TrackAllProperties(); 17 | 18 | EntityTracker.TrackAllProperties(); 19 | 20 | EntityTracker.TrackAllProperties(); 21 | 22 | EntityTracker.TrackAllProperties(); 23 | 24 | EntityTracker.TrackAllProperties(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Voyage.Database/Scripts/Seed/ClientRole.sql: -------------------------------------------------------------------------------- 1 | WITH ClientRole_CTE 2 | AS ( 3 | SELECT * 4 | FROM ( 5 | VALUES 6 | (N'2885FDDD-9F03-48EB-8762-9BD176EBB496', N'927fe4a9-4e27-4635-a208-eb5afc953294', N'f000aeed-5158-451d-b704-b1503f6af796') 7 | ,(N'26C092FC-64DB-4334-87B5-09428F025C9C', N'1cd39193-1f83-4d44-ab45-68c85be2acc8', N'f000aeed-5158-461d-b704-b1503f6af797') 8 | ) AS ClientRole([ClientId], [RoleId], [Id]) 9 | ) 10 | -- Reference Data for Role 11 | MERGE INTO dbo.[ClientRole] AS [Target] 12 | USING ClientRole_CTE AS [Source] 13 | ON [Target].[ClientId] = [Source].[ClientId] and [Target].[RoleId] = [Source].[RoleId] and [Target].[Id] = [Source].[Id] 14 | WHEN NOT MATCHED BY TARGET 15 | THEN 16 | INSERT ( 17 | [ClientId],[RoleId],[Id] 18 | ) 19 | VALUES ( 20 | [Source].[ClientId] 21 | ,[Source].[RoleId] 22 | ,[Source].[Id] 23 | ) 24 | OUTPUT $action, inserted.*, deleted.*; 25 | -------------------------------------------------------------------------------- /Voyage.Models/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Voyage.Api.UserManager/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Voyage.Core.UnitTests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Voyage.Database/Scripts/Post-Deployment.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Post-Deployment Script Template 3 | -------------------------------------------------------------------------------------- 4 | This file contains SQL statements that will be appended to the build script. 5 | Use SQLCMD syntax to include a file in the post-deployment script. 6 | Example: :r .\myfile.sql 7 | Use SQLCMD syntax to reference a variable in the post-deployment script. 8 | Example: :setvar TableName MyTable 9 | SELECT * FROM [$(TableName)] 10 | -------------------------------------------------------------------------------------- 11 | */ 12 | :r .\Seed\Role.sql 13 | :r .\Seed\User.sql 14 | :r .\Seed\RoleClaim.sql 15 | :r .\Seed\UserPhone.sql 16 | :r .\Seed\UserRole.sql 17 | :r .\Seed\Client.sql 18 | :r .\Seed\ClientRole.sql 19 | :r .\Seed\ClientScopeType.sql 20 | :r .\Seed\ClientScope.sql 21 | :r .\Seed\Notification.sql 22 | :r .\Seed\Chat.sql -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Validators/UserPhoneModelValidatorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.TestHelper; 2 | using Voyage.Models.Validators; 3 | using Xunit; 4 | 5 | namespace Voyage.Models.UnitTests.Validators 6 | { 7 | [Trait("Category", "Model.Validation")] 8 | public class UserPhoneModelValidatorTests 9 | { 10 | private readonly UserPhoneModelValidator _validator; 11 | 12 | public UserPhoneModelValidatorTests() 13 | { 14 | _validator = new UserPhoneModelValidator(); 15 | } 16 | 17 | [Fact] 18 | public void Should_Have_Error_When_PhoneNumber_Null() 19 | { 20 | _validator.ShouldHaveValidationErrorFor(model => model.PhoneNumber, null as string); 21 | } 22 | 23 | [Fact] 24 | public void Should_Have_Error_When_PhoneNumber_Empty() 25 | { 26 | _validator.ShouldHaveValidationErrorFor(model => model.PhoneNumber, string.Empty); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Voyage.Services/Role/IRoleService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Voyage.Models; 4 | using Microsoft.AspNet.Identity; 5 | 6 | namespace Voyage.Services.Role 7 | { 8 | public interface IRoleService 9 | { 10 | Task> GetRoleClaimsByRoleIdAsync(string id); 11 | 12 | Task GetRoleByNameAsync(string name); 13 | 14 | RoleModel GetRoleById(string id); 15 | 16 | Task CreateRoleAsync(RoleModel model); 17 | 18 | Task> GetRolesAsync(); 19 | 20 | Task> GetRoleClaimsAsync(string name); 21 | 22 | Task AddClaimAsync(string roleId, ClaimModel claim); 23 | 24 | Task RemoveRoleAsync(string roleId); 25 | 26 | Task RemoveClaimAsync(string roleId, int claimId); 27 | 28 | Task GetClaimByIdAsync(string roleId, int claimId); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ActivityAudit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Voyage.Models.Entities 6 | { 7 | [Table("ActivityAudit")] 8 | public class ActivityAudit 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int Id { get; set; } 13 | 14 | [MaxLength(64)] 15 | public string RequestId { get; set; } 16 | 17 | [MaxLength(32)] 18 | public string Method { get; set; } 19 | 20 | [MaxLength(128)] 21 | public string Path { get; set; } 22 | 23 | [MaxLength(64)] 24 | public string IpAddress { get; set; } 25 | 26 | public DateTime Date { get; set; } 27 | 28 | public int StatusCode { get; set; } 29 | 30 | public string Error { get; set; } 31 | 32 | [MaxLength(50)] 33 | public string UserName { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Voyage.Core/Exceptions/ApiException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Voyage.Core.Exceptions 5 | { 6 | public class ApiException : Exception 7 | { 8 | public ApiException(HttpStatusCode statusCode, string message, Exception ex) 9 | : base(message, ex) 10 | { 11 | StatusCode = statusCode; 12 | } 13 | 14 | public ApiException(HttpStatusCode statusCode, string message) 15 | : base(message) 16 | { 17 | StatusCode = statusCode; 18 | } 19 | 20 | public ApiException(HttpStatusCode statusCode, string errorCode, string errorDescription) 21 | : base($"{errorCode}::{errorDescription}") 22 | { 23 | StatusCode = statusCode; 24 | } 25 | 26 | public ApiException(HttpStatusCode statusCode) 27 | { 28 | StatusCode = statusCode; 29 | } 30 | 31 | public HttpStatusCode StatusCode { get; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Voyage.Models/Validators/UserPhoneModelValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentValidation; 3 | using Voyage.Core; 4 | using Voyage.Models.Enum; 5 | 6 | namespace Voyage.Models.Validators 7 | { 8 | public class UserPhoneModelValidator : AbstractValidator 9 | { 10 | public UserPhoneModelValidator() 11 | { 12 | RuleFor(_ => _.PhoneNumber) 13 | .NotEmpty() 14 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "Phone number is a required field"); 15 | 16 | RuleFor(_ => _.PhoneType) 17 | .NotEmpty() 18 | .Must(type => new List { PhoneType.Mobile.ToString(), PhoneType.Office.ToString(), PhoneType.Home.ToString(), PhoneType.Other.ToString() }.Contains(type)) 19 | .WithErrorCodeMessage(Constants.ErrorCodes.InvalidPhoneNumber, "Invalid phone type. Must be one of 'Mobile', 'Office', 'Home', 'Other'."); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Voyage.Models/UserModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentValidation.Attributes; 3 | using Newtonsoft.Json; 4 | using Voyage.Models.Validators; 5 | using Embarr.WebAPI.AntiXss; 6 | 7 | namespace Voyage.Models 8 | { 9 | [Validator(typeof(UserModelValidator))] 10 | public class UserModel 11 | { 12 | [AntiXss] 13 | public string Id { get; set; } 14 | 15 | [AntiXss] 16 | public string FirstName { get; set; } 17 | 18 | [AntiXss] 19 | public string LastName { get; set; } 20 | 21 | [AntiXss] 22 | public string Username { get; set; } 23 | 24 | [AntiXss] 25 | public string Email { get; set; } 26 | 27 | public List Phones { get; set; } 28 | 29 | public bool IsActive { get; set; } 30 | 31 | public bool IsVerifyRequired { get; set; } 32 | 33 | [AntiXss] 34 | [JsonIgnore] 35 | public string PasswordRecoveryToken { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Validators/ClaimModelValidatorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.TestHelper; 2 | using Voyage.Models.UnitTests.Common; 3 | using Voyage.Models.Validators; 4 | using Xunit; 5 | 6 | namespace Voyage.Models.UnitTests.Validators 7 | { 8 | [Trait("Category", "Model.Validation")] 9 | public class ClaimModelValidatorTests : BaseUnitTest 10 | { 11 | private readonly ClaimModelValidator _validator; 12 | 13 | public ClaimModelValidatorTests() 14 | { 15 | _validator = new ClaimModelValidator(); 16 | } 17 | 18 | [Fact] 19 | public void Should_Have_Error_When_ClaimType_Is_Null() 20 | { 21 | _validator.ShouldHaveValidationErrorFor(claim => claim.ClaimType, null as string); 22 | } 23 | 24 | [Fact] 25 | public void Should_Have_Error_When_ClaimValue_Is_Null() 26 | { 27 | _validator.ShouldHaveValidationErrorFor(claim => claim.ClaimValue, null as string); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Voyage.Web/Content/ripples.css: -------------------------------------------------------------------------------- 1 | .withripple{position:relative}.ripple-container{position:absolute;top:0;left:0;z-index:1;width:100%;height:100%;overflow:hidden;border-radius:inherit;pointer-events:none}.ripple{position:absolute;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:100%;background-color:#000;background-color:rgba(0,0,0,.05);-webkit-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);-webkit-transform-origin:50%;-ms-transform-origin:50%;-o-transform-origin:50%;transform-origin:50%;opacity:0;pointer-events:none}.ripple.ripple-on{-webkit-transition:opacity .15s ease-in 0s,-webkit-transform .5s cubic-bezier(.4,0,.2,1) .1s;-o-transition:opacity .15s ease-in 0s,-o-transform .5s cubic-bezier(.4,0,.2,1) .1s;transition:opacity .15s ease-in 0s,transform .5s cubic-bezier(.4,0,.2,1) .1s;opacity:.1}.ripple.ripple-out{-webkit-transition:opacity .1s linear 0s!important;-o-transition:opacity .1s linear 0s!important;transition:opacity .1s linear 0s!important;opacity:0} 2 | /*# sourceMappingURL=ripples.min.css.map */ -------------------------------------------------------------------------------- /Voyage.Models/Validators/UserModelValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Voyage.Core; 3 | 4 | namespace Voyage.Models.Validators 5 | { 6 | public class UserModelValidator : AbstractValidator 7 | { 8 | public UserModelValidator() 9 | { 10 | RuleFor(_ => _.FirstName) 11 | .NotEmpty() 12 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "First name is a required field"); 13 | 14 | RuleFor(_ => _.LastName) 15 | .NotEmpty() 16 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "Last name is a required field"); 17 | 18 | RuleFor(_ => _.Username) 19 | .NotEmpty() 20 | .WithErrorCodeMessage(Constants.ErrorCodes.MissingField, "Username is a required field"); 21 | 22 | RuleFor(_ => _.Email) 23 | .EmailAddress() 24 | .WithErrorCodeMessage(Constants.ErrorCodes.InvalidEmail, "Email is invalid"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Voyage.Web.UnitTests/Middleware/TestEnvironmentMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Microsoft.Owin; 4 | 5 | namespace Voyage.Web.UnitTests.Middleware 6 | { 7 | /// 8 | /// Request ID is not being populated into the environment dictionary. For these tests, add this fake 9 | /// middleware in to compensate. 10 | /// 11 | public class TestEnvironmentMiddleware : OwinMiddleware 12 | { 13 | private readonly Dictionary _env; 14 | 15 | public TestEnvironmentMiddleware(OwinMiddleware next, Dictionary env) 16 | : base(next) 17 | { 18 | _env = env; 19 | } 20 | 21 | public override async Task Invoke(IOwinContext context) 22 | { 23 | foreach (var pair in _env) 24 | { 25 | context.Environment.Add(pair.Key, pair.Value); 26 | } 27 | 28 | await Next.Invoke(context); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Voyage.Core.UnitTests/ExceptionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Voyage.Core.UnitTests.Common; 4 | using Xunit; 5 | 6 | namespace Voyage.Core.UnitTests 7 | { 8 | public class ExceptionExtensionsTests : BaseUnitTest 9 | { 10 | [Fact] 11 | public void FlattenMessages_Should_Stop_At_Depth_5() 12 | { 13 | var aggregateException = new AggregateException("1", new Exception("2", new Exception("3", new Exception("4", new Exception("5", new Exception("6")))))); 14 | var messages = aggregateException.FlattenMessages(); 15 | messages.Should().NotContain("6"); 16 | } 17 | 18 | [Fact] 19 | public void FlattenMessages_Should_Concatonate_Inner_Exception_Messages() 20 | { 21 | var aggregateException = new AggregateException("1", new Exception("2", new Exception("3"))); 22 | var messages = aggregateException.FlattenMessages(); 23 | messages.Should().Be("1\r\n2\r\n3\r\n"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/Content/ripples.css: -------------------------------------------------------------------------------- 1 | .withripple{position:relative}.ripple-container{position:absolute;top:0;left:0;z-index:1;width:100%;height:100%;overflow:hidden;border-radius:inherit;pointer-events:none}.ripple{position:absolute;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:100%;background-color:#000;background-color:rgba(0,0,0,.05);-webkit-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);-webkit-transform-origin:50%;-ms-transform-origin:50%;-o-transform-origin:50%;transform-origin:50%;opacity:0;pointer-events:none}.ripple.ripple-on{-webkit-transition:opacity .15s ease-in 0s,-webkit-transform .5s cubic-bezier(.4,0,.2,1) .1s;-o-transition:opacity .15s ease-in 0s,-o-transform .5s cubic-bezier(.4,0,.2,1) .1s;transition:opacity .15s ease-in 0s,transform .5s cubic-bezier(.4,0,.2,1) .1s;opacity:.1}.ripple.ripple-out{-webkit-transition:opacity .1s linear 0s!important;-o-transition:opacity .1s linear 0s!important;transition:opacity .1s linear 0s!important;opacity:0} 2 | /*# sourceMappingURL=ripples.min.css.map */ -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | 19 | .checkbox input[type=checkbox]:checked+.checkbox-material .check, label.checkbox-inline input[type=checkbox]:checked+.checkbox-material .check { 20 | color: #4285f4 !important; 21 | border-color: #4285f4 !important; 22 | } 23 | 24 | .checkbox input[type=checkbox]:checked+.checkbox-material .check:before, label.checkbox-inline input[type=checkbox]:checked+.checkbox-material .check:before { 25 | color: #4285f4 !important; 26 | -webkit-box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px; 27 | box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px; 28 | } -------------------------------------------------------------------------------- /Voyage.Services/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Voyage.Database/dbo/Tables/User.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[User] ( 2 | [Id] NVARCHAR (128) NOT NULL, 3 | [FirstName] NVARCHAR (128) NOT NULL, 4 | [LastName] NVARCHAR (128) NOT NULL, 5 | [IsActive] BIT NOT NULL, 6 | [Email] NVARCHAR (MAX) NULL, 7 | [EmailConfirmed] BIT NOT NULL, 8 | [PasswordHash] NVARCHAR (MAX) NULL, 9 | [SecurityStamp] NVARCHAR (MAX) NULL, 10 | [PhoneNumber] NVARCHAR (MAX) NULL, 11 | [PhoneNumberConfirmed] BIT NOT NULL, 12 | [TwoFactorEnabled] BIT NOT NULL, 13 | [LockoutEndDateUtc] DATETIME NULL, 14 | [LockoutEnabled] BIT NOT NULL, 15 | [AccessFailedCount] INT NOT NULL, 16 | [UserName] NVARCHAR (MAX) NULL, 17 | [Deleted] BIT DEFAULT ((0)) NOT NULL, 18 | [IsVerifyRequired ] BIT NOT NULL DEFAULT ((0)), 19 | [PasswordRecoveryToken] NVARCHAR(MAX) NULL, 20 | CONSTRAINT [PK_dbo.Users] PRIMARY KEY CLUSTERED ([Id] ASC) 21 | ); 22 | 23 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using Microsoft.AspNet.Identity.EntityFramework; 5 | 6 | namespace Voyage.Models.Entities 7 | { 8 | /// 9 | /// Represents an application user. Properties placed on this class will be persisted to the user table 10 | /// 11 | [Table("User")] 12 | public class ApplicationUser : IdentityUser, ISoftDeleteable 13 | { 14 | [MaxLength(128)] 15 | [Required] 16 | public string FirstName { get; set; } 17 | 18 | [Required] 19 | [MaxLength(128)] 20 | public string LastName { get; set; } 21 | 22 | public virtual ICollection Phones { get; set; } 23 | 24 | [Required] 25 | public bool IsActive { get; set; } 26 | 27 | [Required] 28 | public bool Deleted { get; set; } 29 | 30 | [Required] 31 | public bool IsVerifyRequired { get; set; } 32 | 33 | public string PasswordRecoveryToken { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ClientScopeType.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace Voyage.Models.Entities 7 | { 8 | [Table("ClientScopeType")] 9 | public class ClientScopeType 10 | { 11 | [AntiXss] 12 | [Key] 13 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 14 | public string Id { get; set; } 15 | 16 | [AntiXss] 17 | [Required] 18 | public string Name { get; set; } 19 | 20 | [AntiXss] 21 | [Required] 22 | public string Description { get; set; } 23 | 24 | [AntiXss] 25 | [Required] 26 | public string CreatedBy { get; set; } 27 | 28 | [AntiXss] 29 | [Required] 30 | public string LastModifiedBy { get; set; } 31 | 32 | [Required] 33 | public DateTime LastModifiedDate { get; set; } 34 | 35 | [Required] 36 | public DateTime CreatedDate { get; set; } 37 | 38 | [Required] 39 | public bool IsDeleted { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Voyage.Api/Middleware/Processors/ErrorResponseProcessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | 3 | namespace Voyage.Api.Middleware.Processors 4 | { 5 | /// 6 | /// Reads a the response as a string if it was an error 7 | /// 8 | /// 9 | /// This assumes the write-only stream has been replaced 10 | /// with a read-write stream 11 | /// 12 | public class ErrorResponseProcessor : ResponseProcessor 13 | { 14 | public override bool ShouldProcess(IOwinResponse response) 15 | { 16 | return 17 | response.Body.CanSeek && // Can the steam seek? 18 | response.Body.CanRead && // can the stream read? 19 | response.StatusCode >= 400 && // Request errors 20 | response.StatusCode <= 599 && // Server errors 21 | response.ContentLength > 0 && ( // Readable content 22 | response.ContentType.StartsWith("application/json") || 23 | response.ContentType.StartsWith("application/xml") || 24 | response.ContentType.StartsWith("text/")); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Voyage.Web/Middleware/Processors/ErrorResponseProcessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | 3 | namespace Voyage.Web.Middleware.Processors 4 | { 5 | /// 6 | /// Reads a the response as a string if it was an error 7 | /// 8 | /// 9 | /// This assumes the write-only stream has been replaced 10 | /// with a read-write stream 11 | /// 12 | public class ErrorResponseProcessor : ResponseProcessor 13 | { 14 | public override bool ShouldProcess(IOwinResponse response) 15 | { 16 | return 17 | response.Body.CanSeek && // Can the steam seek? 18 | response.Body.CanRead && // can the stream read? 19 | response.StatusCode >= 400 && // Request errors 20 | response.StatusCode <= 599 && // Server errors 21 | response.ContentLength > 0 && ( // Readable content 22 | response.ContentType.StartsWith("application/json") || 23 | response.ContentType.StartsWith("application/xml") || 24 | response.ContentType.StartsWith("text/")); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Voyage.Data/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Map/Profiles/RoleProfileTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Voyage.Models.Entities; 3 | using Voyage.Models.UnitTests.Common; 4 | using Voyage.Models.UnitTests.Common.AutoMapperFixture; 5 | using Xunit; 6 | 7 | namespace Voyage.Models.UnitTests.Map.Profiles 8 | { 9 | [Collection(AutoMapperCollection.CollectionName)] 10 | public class RoleProfileTests : BaseUnitTest 11 | { 12 | private readonly AutoMapperFixture _mappingFixture; 13 | 14 | public RoleProfileTests(AutoMapperFixture mappingFixture) 15 | { 16 | _mappingFixture = mappingFixture; 17 | } 18 | 19 | [Fact] 20 | public void ApplicationRole_Should_Map_To_RoleModel() 21 | { 22 | var appRole = new ApplicationRole 23 | { 24 | Name = "role1", 25 | Id = "123" 26 | }; 27 | 28 | var role = _mappingFixture.MapperInstance.Map(appRole); 29 | 30 | role.Should().NotBeNull(); 31 | role.Name.Should().Be(appRole.Name); 32 | role.Id.Should().Be(appRole.Id); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Voyage.Services/Notification/Push/SignalRContractResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.AspNet.SignalR.Infrastructure; 4 | using Newtonsoft.Json.Serialization; 5 | 6 | namespace Voyage.Services.Notification.Push 7 | { 8 | public class SignalRContractResolver : IContractResolver 9 | { 10 | private readonly Assembly _assembly; 11 | private readonly IContractResolver _camelCaseContractResolver; 12 | private readonly IContractResolver _defaultContractSerializer; 13 | 14 | public SignalRContractResolver() 15 | { 16 | _defaultContractSerializer = new DefaultContractResolver(); 17 | _camelCaseContractResolver = new CamelCasePropertyNamesContractResolver(); 18 | _assembly = typeof(Connection).Assembly; 19 | } 20 | 21 | public JsonContract ResolveContract(Type type) 22 | { 23 | if (type.Assembly.Equals(_assembly)) 24 | { 25 | return _defaultContractSerializer.ResolveContract(type); 26 | } 27 | 28 | return _camelCaseContractResolver.ResolveContract(type); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Voyage.Web/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | .checkbox input[type=checkbox]:checked+.checkbox-material .check, label.checkbox-inline input[type=checkbox]:checked+.checkbox-material .check { 13 | color: #4285f4 !important; 14 | border-color: #4285f4 !important; 15 | } 16 | 17 | .checkbox input[type=checkbox]:checked+.checkbox-material .check:before, label.checkbox-inline input[type=checkbox]:checked+.checkbox-material .check:before { 18 | color: #4285f4 !important; 19 | -webkit-box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px; 20 | box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px; 21 | } 22 | 23 | .primary-color { 24 | background-color: #2196f3 !important; 25 | } 26 | 27 | .secondary-color { 28 | background-color:white !important; 29 | color: black !important; 30 | } 31 | 32 | .full-width { 33 | width: 100% !important; 34 | } -------------------------------------------------------------------------------- /VoyageSpecFlow/Step_Definitions/VoyageStepDefinitions/SpecFlowFeatureVoyageCalculatorStepDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using TechTalk.SpecFlow; 6 | 7 | namespace VoyageSpecFlow.VoyageStepDefinitions 8 | { 9 | [Binding] 10 | public sealed class SpecFlowFeatureVoyageStepDefinition 11 | { 12 | // For additional details on SpecFlow step definitions see http://go.specflow.org/doc-stepdef 13 | 14 | [Given("I have entered (.*) into the calculator")] 15 | public void GivenIHaveEnteredSomethingIntoTheCalculator(int numbers) 16 | { 17 | Console.WriteLine(numbers); 18 | } 19 | 20 | [When("I press add")] 21 | public void WhenIPressAdd() 22 | { 23 | Console.WriteLine("Add button pressed"); 24 | } 25 | 26 | [Then("the result should be (.*) on the screen")] 27 | public void ThenTheResultShouldBe(int result) 28 | { 29 | if(result == 120) 30 | Console.WriteLine("Test Passed"); 31 | Console.WriteLine("Test Failed"); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Voyage.Data/Stores/CustomUserStore.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | using System.Threading.Tasks; 3 | using Voyage.Models.Entities; 4 | using Microsoft.AspNet.Identity.EntityFramework; 5 | 6 | namespace Voyage.Data.Stores 7 | { 8 | public class CustomUserStore : UserStore 9 | { 10 | public CustomUserStore(DbContext context) 11 | : base(context) 12 | { 13 | } 14 | 15 | public override Task FindByEmailAsync(string email) 16 | { 17 | return GetUserAggregateAsync(u => u.Email.ToUpper() == email.ToUpper() && !u.Deleted); 18 | } 19 | 20 | public override Task FindByIdAsync(string userId) 21 | { 22 | return GetUserAggregateAsync(u => u.Id.Equals(userId) && !u.Deleted); 23 | } 24 | 25 | public override async Task FindByNameAsync(string userName) 26 | { 27 | var user = await GetUserAggregateAsync(u => u.UserName.ToUpper() == userName.ToUpper() && !u.Deleted); 28 | return user; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /deploy-package.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: Need to install - https://msdn.microsoft.com/en-us/library/mt204009.aspx 4 | :: DacFx 5 | 6 | 7 | SETLOCAL ENABLEEXTENSIONS 8 | 9 | :: root is the folder containing this script (without trailing backslash) 10 | set root=%~dp0 11 | set root=%root:~0,-1% 12 | 13 | ::Path to the SqlPackage.exe 14 | SET toolPath="C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\130\SqlPackage.exe" 15 | 16 | ::Path to the dacpac 17 | SET sourcePath=".\Voyage.Database\bin\Debug\Voyage.Database.dacpac" 18 | 19 | ::Connection string for the target 20 | SET connectionString="Integrated Security=SSPI;Persist Security Info=False;Data Source=localhost;Initial Catalog=Voyage" 21 | 22 | :: process command line 23 | if not [%1]==[] if not [%1]==[-] set toolPath=%1 24 | if not [%2]==[] if not [%2]==[-] set sourcePath=%2 25 | if not [%3]==[] if not [%3]==[-] set connectionString=%3 26 | 27 | 28 | :: report configuration 29 | echo connectionString: %connectionString% 30 | echo sourcePath: %sourcePath% 31 | echo toolPath: %toolPath% 32 | 33 | %toolPath% /TargetConnectionString:%connectionString% /SourceFile:%sourcePath% /Action:Publish 34 | 35 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Validators/RoleModelValidatorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.TestHelper; 2 | using Voyage.Models.UnitTests.Common; 3 | using Voyage.Models.Validators; 4 | using Xunit; 5 | 6 | namespace Voyage.Models.UnitTests.Validators 7 | { 8 | [Trait("Category", "Model.Validation")] 9 | public class RoleModelValidatorTests : BaseUnitTest 10 | { 11 | private readonly RoleModelValidator _validator; 12 | 13 | public RoleModelValidatorTests() 14 | { 15 | _validator = new RoleModelValidator(); 16 | } 17 | 18 | [Fact] 19 | public void Should_Have_Error_When_Name_Is_Null() 20 | { 21 | _validator.ShouldHaveValidationErrorFor(role => role.Name, null as string); 22 | } 23 | 24 | [Fact] 25 | public void Should_Have_Error_When_Name_Is_Empty() 26 | { 27 | _validator.ShouldHaveValidationErrorFor(role => role.Name, string.Empty); 28 | } 29 | 30 | [Fact] 31 | public void Should_Not_Have_Error_When_Name_Populated() 32 | { 33 | _validator.ShouldNotHaveValidationErrorFor(role => role.Name, "Role Name"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /readme_docs/STANDARDS-TESTING.md: -------------------------------------------------------------------------------- 1 | ## Testing Standards 2 | 3 | ## Table of Contents 4 | * abc 5 | * def 6 | 7 | ### Server Testing 8 | 1. Integration tests exist for the Voyage.Data 9 | - The primary goal of these tests is to excercise the EntityFramework configuration and repositories 10 | 2. There should be a mirrored structure between project files and test files 11 | - This allows the developer to locate the test file quickly based on the location of the project file 12 | 3. The tests use behavior and state verification 13 | - Use MOQ to setup expectations and verify the behavior 14 | - There is a BaseUnitTest class which provides a MockRepository and AutFixture.Fixture member to the 15 | derived class 16 | 4. Use interfaces for dependencies in order to loosely couple components and make code mock-able 17 | 5. When possible avoid statics to promote testable components 18 | 19 | ### Testing Stack 20 | The current testing stack for the server is found below. 21 | 22 | #### Server Tests 23 | 1. [Xunit](https://xunit.github.io/) 24 | 2. [AutoFixture](https://github.com/AutoFixture/AutoFixture) 25 | 3. [FluentAssertions](http://www.fluentassertions.com/) 26 | 4. [MOQ](https://github.com/moq/moq4) 27 | -------------------------------------------------------------------------------- /Voyage.Data/IVoyageDataContext.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | using System.Threading.Tasks; 3 | using Voyage.Models.Entities; 4 | 5 | namespace Voyage.Data 6 | { 7 | public interface IVoyageDataContext 8 | { 9 | IDbSet Logs { get; set; } 10 | 11 | IDbSet RoleClaims { get; set; } 12 | 13 | IDbSet Users { get; set; } 14 | 15 | IDbSet ActivityAudits { get; set; } 16 | 17 | IDbSet UserPhones { get; set; } 18 | 19 | IDbSet Clients { get; set; } 20 | 21 | IDbSet ClientRoles { get; set; } 22 | 23 | IDbSet ClientScopes { get; set; } 24 | 25 | IDbSet ClientScopeTypes { get; set; } 26 | 27 | IDbSet Notifications { get; set; } 28 | 29 | IDbSet ProfileImages { get; set; } 30 | 31 | IDbSet ChatChannels { get; set; } 32 | 33 | IDbSet ChatMessages { get; set; } 34 | 35 | IDbSet ChatChannelMembers { get; set; } 36 | 37 | int SaveChanges(); 38 | 39 | Task SaveChangesAsync(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Voyage.Models/Entities/ClientScope.cs: -------------------------------------------------------------------------------- 1 | using Embarr.WebAPI.AntiXss; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace Voyage.Models.Entities 7 | { 8 | [Table("ClientScope")] 9 | public class ClientScope 10 | { 11 | [AntiXss] 12 | [Key] 13 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 14 | public string Id { get; set; } 15 | 16 | [AntiXss] 17 | [Required] 18 | public string ClientId { get; set; } 19 | 20 | [AntiXss] 21 | [Required] 22 | public string ClientScopeTypeId { get; set; } 23 | 24 | [AntiXss] 25 | [Required] 26 | public string CreatedBy { get; set; } 27 | 28 | [AntiXss] 29 | [Required] 30 | public string LastModifiedBy { get; set; } 31 | 32 | [Required] 33 | public DateTime LastModifiedDate { get; set; } 34 | 35 | [Required] 36 | public DateTime CreatedDate { get; set; } 37 | 38 | [Required] 39 | public bool IsDeleted { get; set; } 40 | 41 | public virtual ClientScopeType ClientScopeType { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Voyage.Services/ApplicationInfo/ApplicationInfoService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Voyage.Core; 4 | using Voyage.Models; 5 | using Voyage.Services.FileReader; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace Voyage.Services.ApplicationInfo 9 | { 10 | public class ApplicationInfoService : IApplicationInfoService 11 | { 12 | private readonly IFileReaderService _fileReaderService; 13 | private readonly string _fileName; 14 | 15 | public ApplicationInfoService(IFileReaderService fileReaderService, string fileName) 16 | { 17 | _fileReaderService = fileReaderService.ThrowIfNull(nameof(fileReaderService)); 18 | _fileName = fileName.ThrowIfNullOrEmpty(nameof(fileName)); 19 | } 20 | 21 | public ApplicationInfoModel GetApplicationInfo() 22 | { 23 | var text = _fileReaderService.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _fileName)); 24 | 25 | var buildNumber = (string)JObject.Parse(text).SelectToken("build.buildNumber"); 26 | 27 | return new ApplicationInfoModel 28 | { 29 | BuildNumber = buildNumber 30 | }; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/ApplicationInfoServiceTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Moq; 3 | using Voyage.Services.ApplicationInfo; 4 | using Voyage.Services.FileReader; 5 | using Voyage.Services.UnitTests.Common; 6 | using Xunit; 7 | 8 | namespace Voyage.Services.UnitTests 9 | { 10 | public class ApplicationInfoServiceTests : BaseUnitTest 11 | { 12 | private readonly ApplicationInfoService _applicationInfoService; 13 | 14 | public ApplicationInfoServiceTests() 15 | { 16 | const string fileName = "MyFile"; 17 | var fileReaderService = new Mock(); 18 | 19 | fileReaderService.Setup(_ => 20 | _.ReadAllText(It.IsAny())) 21 | .Returns("{ 'build': { 'buildNumber': 'some_number'}}"); 22 | 23 | _applicationInfoService = new ApplicationInfoService(fileReaderService.Object, fileName); 24 | } 25 | 26 | [Fact] 27 | public void GetApplicationInfo_Should_Read_Application_Info() 28 | { 29 | var result = _applicationInfoService.GetApplicationInfo(); 30 | 31 | result.BuildNumber.Should().BeOfType().And.Be("some_number"); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/TestDbAsyncEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data.Entity.Infrastructure; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Voyage.Services.UnitTests.Common 7 | { 8 | /// 9 | /// Used to create an in-memory DbAsyncQueryProvider so we can unit test Async methods. 10 | /// See https://msdn.microsoft.com/en-us/data/dn314429 11 | /// 12 | public class TestDbAsyncEnumerator : IDbAsyncEnumerator 13 | { 14 | private readonly IEnumerator _inner; 15 | 16 | public TestDbAsyncEnumerator(IEnumerator inner) 17 | { 18 | _inner = inner; 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _inner.Dispose(); 24 | } 25 | 26 | public Task MoveNextAsync(CancellationToken cancellationToken) 27 | { 28 | return Task.FromResult(_inner.MoveNext()); 29 | } 30 | 31 | public T Current 32 | { 33 | get { return _inner.Current; } 34 | } 35 | 36 | object IDbAsyncEnumerator.Current 37 | { 38 | get { return Current; } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /VoyageSpecFlow/Features/VoyageStepfeatures/SpecFlowFeatureRoleBaseAuthentication.feature: -------------------------------------------------------------------------------- 1 | Feature: Voyage resource assessed & access denied 2 | In order to get access to protected sections of the site 3 | A user 4 | Should be able to get an access to a resource based on the access it possess 5 | 6 | Scenario: User do not have access to a resource 7 | Given I do not have access to resource 8 | When I access the resource url 'http://url' 9 | Then I see access denied to resource message 10 | 11 | Scenario: User logged in successfully and I have access to the requested resource 12 | Given I exist as a user 13 | When I sign in with valid credentials 14 | Then I should be signed in 15 | And I should be on home page 16 | When I access the resource url 'http://url' 17 | Then I get access to the resouces and I see the requested resource 18 | 19 | Scenario: User logged in successfully and I dont have access to the requested resource 20 | Given I exist as a user 21 | When I sign in with valid credentials 22 | Then I should be signed in 23 | And I should be on home page 24 | When I request for an access to a resource 25 | Then I get access denied 402 exception and the exception is propogated to the UI -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/UserPhoneRepositoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Voyage.Data.Repositories.UserPhone; 5 | using Voyage.Data.UnitTests.Common; 6 | using Voyage.Models.Entities; 7 | using Xunit; 8 | 9 | namespace Voyage.Data.UnitTests 10 | { 11 | public class UserPhoneRepositoryTests : BaseUnitTest 12 | { 13 | private readonly UserPhoneRepository _phoneRepository; 14 | 15 | public UserPhoneRepositoryTests() 16 | { 17 | var mockContext = Mock.Create(); 18 | _phoneRepository = new UserPhoneRepository(mockContext.Object); 19 | } 20 | 21 | [Fact] 22 | public void Add_Should_Throw_NotImplementedException() 23 | { 24 | Func throwAction = async () => await _phoneRepository.AddAsync(new UserPhone()); 25 | throwAction 26 | .ShouldThrow(); 27 | } 28 | 29 | [Fact] 30 | public void GetAll_Should_Throw_NotImplementedException() 31 | { 32 | Action throwAction = () => _phoneRepository.GetAll(); 33 | throwAction 34 | .ShouldThrow(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/ActivityAuditRepositoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Voyage.Data.Repositories.ActivityAudit; 5 | using Voyage.Data.UnitTests.Common; 6 | using Xunit; 7 | 8 | namespace Voyage.Data.UnitTests 9 | { 10 | [Trait("Category", "Auditing")] 11 | public class ActivityAuditRepositoryTests : BaseUnitTest 12 | { 13 | private readonly ActivityAuditRepository _repository; 14 | 15 | public ActivityAuditRepositoryTests() 16 | { 17 | var mockContext = Mock.Create(); 18 | _repository = new ActivityAuditRepository(mockContext.Object); 19 | } 20 | 21 | [Fact] 22 | public void Delete_Should_Throw_NotImpelementedException() 23 | { 24 | Func throwAction = async () => await _repository.DeleteAsync(1); 25 | throwAction.ShouldThrow(); 26 | } 27 | 28 | [Fact] 29 | public void Update_Should_Throw_NotImpelementedException() 30 | { 31 | Func throwAction = async () => await _repository.UpdateAsync(new Voyage.Models.Entities.ActivityAudit()); 32 | throwAction.ShouldThrow(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Voyage.Data/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/TestDbAsyncEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data.Entity.Infrastructure; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Voyage.Services.UnitTests.Common 7 | { 8 | /// 9 | /// Used to create an in-memory DbAsyncQueryProvider so we can unit test Async methods. 10 | /// See https://msdn.microsoft.com/en-us/data/dn314429 11 | /// 12 | public class TestDbAsyncEnumerable : EnumerableQuery, IDbAsyncEnumerable, IQueryable 13 | { 14 | public TestDbAsyncEnumerable(IEnumerable enumerable) 15 | : base(enumerable) 16 | { } 17 | 18 | public TestDbAsyncEnumerable(Expression expression) 19 | : base(expression) 20 | { } 21 | 22 | public IDbAsyncEnumerator GetAsyncEnumerator() 23 | { 24 | return new TestDbAsyncEnumerator(this.AsEnumerable().GetEnumerator()); 25 | } 26 | 27 | IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() 28 | { 29 | return GetAsyncEnumerator(); 30 | } 31 | 32 | IQueryProvider IQueryable.Provider 33 | { 34 | get { return new TestDbAsyncQueryProvider(this); } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Voyage.Services/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /VoyageSpecFlow/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Voyage.Web/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace Voyage.Web 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 18 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js")); 24 | 25 | bundles.Add(new StyleBundle("~/Content/css").Include( 26 | "~/Content/bootstrap.css", 27 | "~/Content/site.css")); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Voyage.Core.UnitTests/BaseUnitTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Moq; 4 | using Ploeh.AutoFixture; 5 | 6 | namespace Voyage.Core.UnitTests.Common 7 | { 8 | public abstract class BaseUnitTest : IDisposable 9 | { 10 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 11 | 12 | /// 13 | /// Provides access to a Mock Repository in unit tests 14 | /// 15 | protected MockRepository Mock; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | 18 | public void Dispose() 19 | { 20 | Mock.VerifyAll(); 21 | } 22 | 23 | protected CancellationToken CreateCancelToken() 24 | { 25 | return new CancellationToken(); 26 | } 27 | 28 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 29 | 30 | /// 31 | /// Provides access to Autofixture in unit tests 32 | /// 33 | protected Fixture Fixture; 34 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 35 | 36 | protected BaseUnitTest() 37 | { 38 | Fixture = new Fixture(); 39 | Mock = new MockRepository(MockBehavior.Strict); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Api.UnitTests/Common/BaseUnitTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Moq; 4 | using Ploeh.AutoFixture; 5 | 6 | namespace Voyage.Api.UnitTests.Common 7 | { 8 | public abstract class BaseUnitTest : IDisposable 9 | { 10 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 11 | 12 | /// 13 | /// Provides access to a Mock Repository in unit tests 14 | /// 15 | protected MockRepository Mock; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | 18 | public void Dispose() 19 | { 20 | Mock.VerifyAll(); 21 | } 22 | 23 | protected CancellationToken CreateCancelToken() 24 | { 25 | return new CancellationToken(); 26 | } 27 | 28 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 29 | 30 | /// 31 | /// Provides access to Autofixture in unit tests 32 | /// 33 | protected Fixture Fixture; 34 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 35 | 36 | protected BaseUnitTest() 37 | { 38 | Fixture = new Fixture(); 39 | Mock = new MockRepository(MockBehavior.Strict); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Data.UnitTests/Common/BaseUnitTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Moq; 4 | using Ploeh.AutoFixture; 5 | 6 | namespace Voyage.Data.UnitTests.Common 7 | { 8 | public abstract class BaseUnitTest : IDisposable 9 | { 10 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 11 | 12 | /// 13 | /// Provides access to a Mock Repository in unit tests 14 | /// 15 | protected MockRepository Mock; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | 18 | public void Dispose() 19 | { 20 | Mock.VerifyAll(); 21 | } 22 | 23 | protected CancellationToken CreateCancelToken() 24 | { 25 | return new CancellationToken(); 26 | } 27 | 28 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 29 | 30 | /// 31 | /// Provides access to Autofixture in unit tests 32 | /// 33 | protected Fixture Fixture; 34 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 35 | 36 | protected BaseUnitTest() 37 | { 38 | Fixture = new Fixture(); 39 | Mock = new MockRepository(MockBehavior.Strict); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Web.UnitTests/Common/BaseUnitTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Moq; 4 | using Ploeh.AutoFixture; 5 | 6 | namespace Voyage.Web.UnitTests.Common 7 | { 8 | public abstract class BaseUnitTest : IDisposable 9 | { 10 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 11 | 12 | /// 13 | /// Provides access to a Mock Repository in unit tests 14 | /// 15 | protected MockRepository Mock; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | 18 | public void Dispose() 19 | { 20 | Mock.VerifyAll(); 21 | } 22 | 23 | protected CancellationToken CreateCancelToken() 24 | { 25 | return new CancellationToken(); 26 | } 27 | 28 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 29 | 30 | /// 31 | /// Provides access to Autofixture in unit tests 32 | /// 33 | protected Fixture Fixture; 34 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 35 | 36 | protected BaseUnitTest() 37 | { 38 | Fixture = new Fixture(); 39 | Mock = new MockRepository(MockBehavior.Strict); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Models.UnitTests/Common/BaseUnitTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Moq; 4 | using Ploeh.AutoFixture; 5 | 6 | namespace Voyage.Models.UnitTests.Common 7 | { 8 | public abstract class BaseUnitTest : IDisposable 9 | { 10 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 11 | 12 | /// 13 | /// Provides access to a Mock Repository in unit tests 14 | /// 15 | protected MockRepository Mock; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | 18 | public void Dispose() 19 | { 20 | Mock.VerifyAll(); 21 | } 22 | 23 | protected CancellationToken CreateCancelToken() 24 | { 25 | return new CancellationToken(); 26 | } 27 | 28 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 29 | 30 | /// 31 | /// Provides access to Autofixture in unit tests 32 | /// 33 | protected Fixture Fixture; 34 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 35 | 36 | protected BaseUnitTest() 37 | { 38 | Fixture = new Fixture(); 39 | Mock = new MockRepository(MockBehavior.Strict); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Api/API/v1/ApplicationInfoController.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Core; 2 | using System.Web.Http; 3 | using Voyage.Services.ApplicationInfo; 4 | using Swashbuckle.Swagger.Annotations; 5 | using Voyage.Models; 6 | 7 | namespace Voyage.Api.API.V1 8 | { 9 | /// 10 | /// Controller that provides information about the application. 11 | /// 12 | [RoutePrefix(Constants.RoutePrefixes.V1)] 13 | [AllowAnonymous] 14 | public class ApplicationInfoController : ApiController 15 | { 16 | private readonly IApplicationInfoService _applicationInfoService; 17 | 18 | /// 19 | /// Constructor for the Application Info Controller. 20 | /// 21 | public ApplicationInfoController(IApplicationInfoService applicationInfoService) 22 | { 23 | _applicationInfoService = applicationInfoService.ThrowIfNull(nameof(applicationInfoService)); 24 | } 25 | 26 | /// 27 | /// Retrieves information about the application. 28 | /// 29 | [Route("statuses")] 30 | [SwaggerResponse(200, "UserModel", typeof(ApplicationInfoModel))] 31 | public IHttpActionResult Get() 32 | { 33 | var appInfo = _applicationInfoService.GetApplicationInfo(); 34 | return Ok(appInfo); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/BaseUnitTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Moq; 4 | using Ploeh.AutoFixture; 5 | 6 | namespace Voyage.Services.UnitTests.Common 7 | { 8 | public abstract class BaseUnitTest : IDisposable 9 | { 10 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 11 | 12 | /// 13 | /// Provides access to a Mock Repository in unit tests 14 | /// 15 | protected MockRepository Mock; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | 18 | public void Dispose() 19 | { 20 | Mock.VerifyAll(); 21 | } 22 | 23 | protected CancellationToken CreateCancelToken() 24 | { 25 | return new CancellationToken(); 26 | } 27 | 28 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 29 | 30 | /// 31 | /// Provides access to Autofixture in unit tests 32 | /// 33 | protected Fixture Fixture; 34 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 35 | 36 | protected BaseUnitTest() 37 | { 38 | Fixture = new Fixture(); 39 | Mock = new MockRepository(MockBehavior.Strict); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Services/Admin/AdminService.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Threading.Tasks; 3 | using Voyage.Core; 4 | using Voyage.Core.Exceptions; 5 | using Voyage.Models; 6 | using Voyage.Services.User; 7 | 8 | namespace Voyage.Services.Admin 9 | { 10 | public class AdminService : IAdminService 11 | { 12 | private readonly IUserService _userService; 13 | 14 | public AdminService(IUserService userService) 15 | { 16 | _userService = userService.ThrowIfNull(nameof(userService)); 17 | } 18 | 19 | public async Task ToggleAccountStatus(string userId, ChangeAccountStatusModel changeAccountStatusModel) 20 | { 21 | if (string.IsNullOrEmpty(userId)) 22 | throw new BadRequestException(HttpStatusCode.BadRequest.ToString(), "Empty User Id"); 23 | 24 | var user = await _userService.GetUserAsync(userId); 25 | 26 | if (changeAccountStatusModel.IsActive.HasValue) 27 | user.IsActive = changeAccountStatusModel.IsActive.Value; 28 | 29 | if (changeAccountStatusModel.IsVerifyRequired.HasValue) 30 | user.IsVerifyRequired = changeAccountStatusModel.IsVerifyRequired.Value; 31 | 32 | var result = await _userService.UpdateUserAsync(userId, user); 33 | return result; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Voyage.Api/Middleware/RewindResponseMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | 5 | namespace Voyage.Api.Middleware 6 | { 7 | /// 8 | /// This middleware will replace the default response.body 9 | /// with a read/write stream. After all other middleware 10 | /// executes it will copy the contents to the default stream 11 | /// and re-assign it to the response.body 12 | /// 13 | public class RewindResponseMiddleware : OwinMiddleware 14 | { 15 | public RewindResponseMiddleware(OwinMiddleware next) 16 | : base(next) 17 | { 18 | } 19 | 20 | public override async Task Invoke(IOwinContext context) 21 | { 22 | using (var pipelineStream = new MemoryStream()) 23 | { 24 | // replace the context response with our buffer 25 | var remoteStream = context.Response.Body; 26 | context.Response.Body = pipelineStream; 27 | 28 | // invoke the rest of the pipeline 29 | await Next.Invoke(context); 30 | 31 | pipelineStream.Seek(0, SeekOrigin.Begin); 32 | await pipelineStream.CopyToAsync(remoteStream); 33 | context.Response.Body = remoteStream; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Voyage.Web/Middleware/RewindResponseMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | 5 | namespace Voyage.Web.Middleware 6 | { 7 | /// 8 | /// This middleware will replace the default response.body 9 | /// with a read/write stream. After all other middleware 10 | /// executes it will copy the contents to the default stream 11 | /// and re-assign it to the response.body 12 | /// 13 | public class RewindResponseMiddleware : OwinMiddleware 14 | { 15 | public RewindResponseMiddleware(OwinMiddleware next) 16 | : base(next) 17 | { 18 | } 19 | 20 | public override async Task Invoke(IOwinContext context) 21 | { 22 | using (var pipelineStream = new MemoryStream()) 23 | { 24 | // replace the context response with our buffer 25 | var remoteStream = context.Response.Body; 26 | context.Response.Body = pipelineStream; 27 | 28 | // invoke the rest of the pipeline 29 | await Next.Invoke(context); 30 | 31 | pipelineStream.Seek(0, SeekOrigin.Begin); 32 | await pipelineStream.CopyToAsync(remoteStream); 33 | context.Response.Body = remoteStream; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /readme_docs/MIDDLEWARE.md: -------------------------------------------------------------------------------- 1 | ## Custom Middleware 2 | Owin allows a developer to define custom middleware. This middleware is registered during the startup of the application 3 | and executes in order in which it is registered. Voyage has a number of custom middleware pieces that are described below. 4 | 5 | ### RewindResponseMiddleware 6 | 7 | #### Purpose 8 | This middleware will replace the default stream with a MemoryStream. This will allow downstream services to read and transform 9 | the response. Once all other middlware has excuted, the MemoryStream contents are written back to the default stream and returned as 10 | the body. Note: the default stream is write-only. 11 | 12 | #### Registration Order 13 | This middleware should be registered as one of the first pieces in the pipeline. Any dependent middleware such as ActivityAuditMiddleware 14 | must be registered after. 15 | 16 | ### ActivityAuditMiddleware 17 | 18 | #### Purpose 19 | This middleware will capture the request and response of API calls. 20 | 21 | #### Registration Order 22 | This middleware must be registered after the RewindResponseMiddleware. Additionally, it should be registered early in the pipeline prior 23 | to any middleware that could terminate the request. This includes the authentication middleware. Failure to register this early in the 24 | pipeline will result in activity not being logged. 25 | -------------------------------------------------------------------------------- /Voyage.Core/Clients.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Voyage.Core 4 | { 5 | public static class Clients 6 | { 7 | public static readonly Client Client1 = new Client 8 | { 9 | Id = "123456", 10 | Secret = "abcdef", 11 | RedirectUrl = "http://localhost:52431/Home/Index", 12 | AllowedScopes = new List 13 | { 14 | "profile", 15 | "email", 16 | "api" 17 | } 18 | }; 19 | 20 | public static readonly Client Client2 = new Client 21 | { 22 | Id = "client-super", 23 | Secret = "secret", 24 | RedirectUrl = "http://localhost:3000/dashboard", 25 | AllowedScopes = new List 26 | { 27 | "profile", 28 | "email", 29 | "api" 30 | } 31 | }; 32 | } 33 | 34 | #pragma warning disable SA1402 // File may only contain a single class 35 | public class Client 36 | #pragma warning restore SA1402 // File may only contain a single class 37 | { 38 | public string Id { get; set; } 39 | 40 | public string Secret { get; set; } 41 | 42 | public string RedirectUrl { get; set; } 43 | 44 | public List AllowedScopes { get; set; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Voyage.Security.BasicToken/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Voyage.Api/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Voyage.Core.UnitTests/ArgumentExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Xunit; 4 | 5 | namespace Voyage.Core.UnitTests 6 | { 7 | public class ArgumentExtensionsTests 8 | { 9 | [Fact] 10 | public void ThrowIfNull_Should_Return_Object_When_Not_Null() 11 | { 12 | var input = new object(); 13 | 14 | var output = input.ThrowIfNull(); 15 | 16 | output.Should().BeSameAs(input); 17 | } 18 | 19 | [Fact] 20 | public void ThrowIfNull_Should_Throw_ArgumentNullException_When_Null() 21 | { 22 | object input = null; 23 | 24 | // ReSharper disable once ExpressionIsAlwaysNull 25 | Action throwAction = () => input.ThrowIfNull(); 26 | 27 | throwAction.ShouldThrow().And.ParamName.Should().Be("Object"); 28 | } 29 | 30 | [Fact] 31 | public void ThrowIfNull_Should_Throw_ArgumentNullException_When_Null_And_Return_nameOfString() 32 | { 33 | object input = null; 34 | const string name = "My Name"; 35 | 36 | // ReSharper disable once ExpressionIsAlwaysNull 37 | Action throwAction = () => input.ThrowIfNull(name); 38 | 39 | throwAction.ShouldThrow().And.ParamName.Should().Be(name); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Voyage.Web/App_Start/Startup.Auth.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.WebApi; 2 | using Voyage.Web.Middleware; 3 | using Microsoft.Owin.Cors; 4 | using Owin; 5 | using System.Web.Http; 6 | using Voyage.Security.BasicToken; 7 | 8 | namespace Voyage.Web 9 | { 10 | public partial class Startup 11 | { 12 | public void Configure(IAppBuilder app) 13 | { 14 | var httpConfig = new HttpConfiguration(); 15 | 16 | // Build the container 17 | ContainerConfig.Register(httpConfig); 18 | 19 | // configure FluentValidation model validator provider 20 | FluentValidationModelValidatorProvider.Configure(httpConfig); 21 | 22 | // 1. Use the autofac scope for owin 23 | app.UseAutofacLifetimeScopeInjector(ContainerConfig.Container); 24 | 25 | // 2. Allow cors requests 26 | app.UseCors(CorsOptions.AllowAll); 27 | 28 | // 3. Use the readable response middleware 29 | app.Use(); 30 | 31 | // 4. Register the activty auditing here so that anonymous activity is captured 32 | app.UseMiddlewareFromContainer(); 33 | 34 | app.UseVoyageOauth2(new VoyageBasicTokenConfiguration 35 | { 36 | Container = ContainerConfig.Container 37 | }); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Voyage.Web/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Voyage.Api/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using Microsoft.Owin.Security.OAuth; 3 | using Newtonsoft.Json.Serialization; 4 | using Voyage.Api.Filters; 5 | 6 | namespace Voyage.Api 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | // Configure Web API to use only bearer token authentication. 14 | config.SuppressDefaultHostAuthentication(); 15 | 16 | config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); 17 | config.Filters.Add(new ValidateModelAttribute()); // Globally configure model validation 18 | config.Filters.Add(new ApiExceptionFilterAttribute()); 19 | 20 | // Set camelcasing on for JSON 21 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 22 | config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; 23 | 24 | // Web API routes 25 | config.MapHttpAttributeRoutes(); 26 | 27 | config.Routes.MapHttpRoute( 28 | name: "DefaultApi", 29 | routeTemplate: "api/{controller}/{id}", 30 | defaults: new { id = RouteParameter.Optional }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Voyage.Security.Oauth2/web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Voyage.Web/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Voyage.Web.Filters; 2 | using Microsoft.Owin.Security.OAuth; 3 | using Newtonsoft.Json.Serialization; 4 | using System.Web.Http; 5 | 6 | namespace Voyage.Web 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | // Configure Web API to use only bearer token authentication. 14 | config.SuppressDefaultHostAuthentication(); 15 | 16 | config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); 17 | config.Filters.Add(new ValidateModelAttribute()); // Globally configure model validation 18 | config.Filters.Add(new ApiExceptionFilterAttribute()); 19 | 20 | // Set camelcasing on for JSON 21 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 22 | config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; 23 | 24 | // Web API routes 25 | config.MapHttpAttributeRoutes(); 26 | 27 | config.Routes.MapHttpRoute( 28 | name: "DefaultApi", 29 | routeTemplate: "api/{controller}/{id}", 30 | defaults: new { id = RouteParameter.Optional }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Voyage.Services.UnitTests/Common/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using System.Data.Entity; 3 | using System.Data.Entity.Infrastructure; 4 | using System.Linq; 5 | 6 | namespace Voyage.Services.UnitTests.Common 7 | { 8 | public static class QueryableExtensions 9 | { 10 | /// 11 | /// Used to mock async calls to DbSets. 12 | /// 13 | public static IDbSet BuildMockDbSet(this IQueryable source) 14 | where T : class 15 | { 16 | var mock = new Mock>(); 17 | mock.As>() 18 | .Setup(x => x.GetAsyncEnumerator()) 19 | .Returns(new TestDbAsyncEnumerator(source.GetEnumerator())); 20 | 21 | mock.As>() 22 | .Setup(x => x.Provider) 23 | .Returns(new TestDbAsyncQueryProvider(source.Provider)); 24 | 25 | mock.As>() 26 | .Setup(x => x.Expression) 27 | .Returns(source.Expression); 28 | 29 | mock.As>() 30 | .Setup(x => x.ElementType) 31 | .Returns(source.ElementType); 32 | 33 | mock.As>() 34 | .Setup(x => x.GetEnumerator()) 35 | .Returns(source.GetEnumerator()); 36 | 37 | return mock.Object; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Voyage.Services/Notification/Push/PushService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.AspNet.SignalR.Infrastructure; 4 | using Voyage.Core; 5 | using Voyage.Models; 6 | 7 | namespace Voyage.Services.Notification.Push 8 | { 9 | public class PushService : IPushService 10 | { 11 | private readonly IConnectionManager _connectionManager; 12 | 13 | public PushService(IConnectionManager connectionManager) 14 | { 15 | _connectionManager = connectionManager.ThrowIfNull(nameof(connectionManager)); 16 | } 17 | 18 | public void PushNotification(NotificationModel model) 19 | { 20 | var context = _connectionManager.GetHubContext(); 21 | 22 | if (string.IsNullOrEmpty(model.AssignedToUserId)) 23 | { 24 | context.Clients.All.newNotification(model); 25 | return; 26 | } 27 | 28 | context.Clients.User(model.AssignedToUserId).newNotification(model); 29 | } 30 | 31 | public void PushChatMessage(IList users, ChatMessageModel model) 32 | { 33 | var context = _connectionManager.GetHubContext(); 34 | 35 | if (!users.Any()) 36 | { 37 | return; 38 | } 39 | 40 | context.Clients.Users(users).newChatMessage(model); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Voyage.Data/Repositories/BaseRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using Voyage.Core; 4 | 5 | namespace Voyage.Data.Repositories 6 | { 7 | /// 8 | /// Abstract implementation of the repository interface 9 | /// 10 | /// Generic TModel which the repository will work with 11 | public abstract class BaseRepository : IRepository 12 | { 13 | #pragma warning disable SA1401 // Fields must be private 14 | #pragma warning disable SA1306 // Field names must begin with lower-case letter 15 | protected IVoyageDataContext Context; 16 | #pragma warning restore SA1306 // Field names must begin with lower-case letter 17 | #pragma warning restore SA1401 // Fields must be private 18 | 19 | protected BaseRepository(IVoyageDataContext context) 20 | { 21 | Context = context.ThrowIfNull(nameof(context)); 22 | } 23 | 24 | public abstract Task AddAsync(TModel model); 25 | 26 | public abstract Task UpdateAsync(TModel model); 27 | 28 | public abstract IQueryable GetAll(); 29 | 30 | public abstract Task GetAsync(object id); 31 | 32 | public abstract Task DeleteAsync(object id); 33 | 34 | public async Task SaveChangesAsync() 35 | { 36 | return await Context.SaveChangesAsync(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Voyage.Api/Middleware/Processors/ResponseProcessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using System; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | namespace Voyage.Api.Middleware.Processors 7 | { 8 | /// 9 | /// Basic response processor 10 | /// 11 | public abstract class ResponseProcessor 12 | { 13 | public abstract bool ShouldProcess(IOwinResponse response); 14 | 15 | public virtual async Task GetResponseStringAsync(IOwinResponse response) 16 | { 17 | if (!response.Body.CanSeek) 18 | throw new Exception("The body does not support seek. Ensure that the RewindResponseMiddleware is registered earlier in the pipeline"); 19 | 20 | if (!ShouldProcess(response)) 21 | throw new Exception("ShouldProcess predicate failed. This processor should not read this type of response"); 22 | 23 | var responseStream = response.Body as MemoryStream; 24 | if (responseStream == null) 25 | { 26 | throw new Exception("The response.body could not be cast as MemoryStream. Ensure that the RewindResponseMiddleware is registered earlier in the pipeline"); 27 | } 28 | 29 | responseStream.Seek(0, SeekOrigin.Begin); 30 | var reader = new StreamReader(responseStream); 31 | string body = await reader.ReadToEndAsync(); 32 | return body; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Voyage.Web/Middleware/Processors/ResponseProcessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using System; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | namespace Voyage.Web.Middleware.Processors 7 | { 8 | /// 9 | /// Basic response processor 10 | /// 11 | public abstract class ResponseProcessor 12 | { 13 | public abstract bool ShouldProcess(IOwinResponse response); 14 | 15 | public virtual async Task GetResponseStringAsync(IOwinResponse response) 16 | { 17 | if (!response.Body.CanSeek) 18 | throw new Exception("The body does not support seek. Ensure that the RewindResponseMiddleware is registered earlier in the pipeline"); 19 | 20 | if (!ShouldProcess(response)) 21 | throw new Exception("ShouldProcess predicate failed. This processor should not read this type of response"); 22 | 23 | var responseStream = response.Body as MemoryStream; 24 | if (responseStream == null) 25 | { 26 | throw new Exception("The response.body could not be cast as MemoryStream. Ensure that the RewindResponseMiddleware is registered earlier in the pipeline"); 27 | } 28 | 29 | responseStream.Seek(0, SeekOrigin.Begin); 30 | var reader = new StreamReader(responseStream); 31 | string body = await reader.ReadToEndAsync(); 32 | return body; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Voyage.Web/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Voyage.Web")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Voyage.Web")] 12 | [assembly: AssemblyCopyright("Copyright © 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("6ec214fc-93ea-40b0-b74d-b147ba640dd4")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | [assembly: AssemblyVersion("1.0.0.0")] 34 | [assembly: AssemblyFileVersion("1.0.0.0")] 35 | -------------------------------------------------------------------------------- /Voyage.Api.UserManager/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | --------------------------------------------------------------------------------