From a44e784ecafaf289e35fcc21b59666e4bef23416 Mon Sep 17 00:00:00 2001 From: LICH010 Date: Mon, 11 Dec 2017 14:14:29 +0600 Subject: [PATCH] n2n/9 without refresh token --- src/N2N.Api/Configuration/AppStart.cs | 12 +- src/N2N.Api/Configuration/TokenConfig.cs | 21 ++ src/N2N.Api/Controllers/UserController.cs | 54 +++- .../Filters/N2NAutorizationFilterAttribute.cs | 77 +++++ src/N2N.Api/N2N.Api.csproj | 19 +- .../Services/AuthentificationService.cs | 146 ++++++++++ .../Services/IAuthentificationService.cs | 20 ++ src/N2N.Api/Startup.cs | 44 ++- src/N2N.Core/Entities/N2NToken.cs | 16 ++ src/N2N.Core/Entities/UserDataForm.cs | 15 - src/N2N.Core/N2N.Core.csproj | 2 +- .../DataContext/N2NDataContext.cs | 2 + .../20171201040445_Initi.Designer.cs | 270 ++++++++++++++++++ .../Migrations/20171201040445_Initi.cs | 31 ++ .../Migrations/N2NDataContextModelSnapshot.cs | 16 +- .../Models/UserRegistrationFormDTO.cs | 9 + .../N2N.Infrasturcture.csproj | 155 +++++----- src/N2N.Infrastructure/app.config | 27 ++ src/N2N.Infrastructure/packages.config | 87 +++--- src/N2N.Services/N2N.Services.csproj | 4 +- src/N2N.Services/app.config | 27 ++ src/N2N.Utilities/N2N.Utilities.csproj | 1 + src/N2N.Web/N2N.Web.csproj | 6 +- src/N2N.Web/src/app/app.module.ts | 11 +- .../src/app/navmenu/navmenu.component.html | 2 + .../src/app/navmenu/navmenu.component.ts | 14 +- .../registration/registration.component.ts | 20 +- src/N2N.Web/src/app/storeHeaders.ts | 22 ++ src/N2N.Web/src/app/storeLinks.ts | 7 + src/N2N.Web/src/app/userService.ts | 60 ++-- tests/N2N.Infrastructure.Tests/App.config | 46 ++- .../N2N.Infrastructure.Tests.csproj | 155 +++++----- .../N2N.Infrastructure.Tests/packages.config | 101 +++---- .../N2N.Utilities.Tests.csproj | 52 ++++ .../Properties/AssemblyInfo.cs | 36 +++ tests/N2N.Utilities.Tests/packages.config | 4 + 36 files changed, 1278 insertions(+), 313 deletions(-) create mode 100644 src/N2N.Api/Configuration/TokenConfig.cs create mode 100644 src/N2N.Api/Filters/N2NAutorizationFilterAttribute.cs create mode 100644 src/N2N.Api/Services/AuthentificationService.cs create mode 100644 src/N2N.Api/Services/IAuthentificationService.cs create mode 100644 src/N2N.Core/Entities/N2NToken.cs delete mode 100644 src/N2N.Core/Entities/UserDataForm.cs create mode 100644 src/N2N.Infrastructure/Migrations/20171201040445_Initi.Designer.cs create mode 100644 src/N2N.Infrastructure/Migrations/20171201040445_Initi.cs create mode 100644 src/N2N.Infrastructure/Models/UserRegistrationFormDTO.cs create mode 100644 src/N2N.Infrastructure/app.config create mode 100644 src/N2N.Services/app.config create mode 100644 src/N2N.Web/src/app/storeHeaders.ts create mode 100644 src/N2N.Web/src/app/storeLinks.ts create mode 100644 tests/N2N.Utilities.Tests/N2N.Utilities.Tests.csproj create mode 100644 tests/N2N.Utilities.Tests/Properties/AssemblyInfo.cs create mode 100644 tests/N2N.Utilities.Tests/packages.config diff --git a/src/N2N.Api/Configuration/AppStart.cs b/src/N2N.Api/Configuration/AppStart.cs index 1fcfdff..87be828 100644 --- a/src/N2N.Api/Configuration/AppStart.cs +++ b/src/N2N.Api/Configuration/AppStart.cs @@ -43,14 +43,13 @@ internal static void UseMvcAndConfigureRoutes(IApplicationBuilder app) /// DI container internal static void IntegrateSimpleInjector(IServiceCollection services, Container container) { - container.Options.DefaultScopedLifestyle = new AspNetRequestLifestyle(); + container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle(); services.AddSingleton(); - services.AddSingleton( - new SimpleInjectorControllerActivator(container)); - services.AddSingleton( - new SimpleInjectorViewComponentActivator(container)); + + services.AddSingleton(new SimpleInjectorControllerActivator(container)); + services.AddSingleton(new SimpleInjectorViewComponentActivator(container)); services.EnableSimpleInjectorCrossWiring(container); services.UseSimpleInjectorAspNetRequestScoping(container); @@ -72,12 +71,15 @@ internal static void InitializeContainer(IApplicationBuilder app, Container cont container.CrossWire(app); container.CrossWire>(app); container.CrossWire>(app); + container.CrossWire(app); // Dependencies container.Register, DbRepository>(); + container.Register, DbRepository>(); container.Register(); container.Register(); container.Register(); + } internal static bool BootstrapDb(N2NDataContext ctx) diff --git a/src/N2N.Api/Configuration/TokenConfig.cs b/src/N2N.Api/Configuration/TokenConfig.cs new file mode 100644 index 0000000..3e0d105 --- /dev/null +++ b/src/N2N.Api/Configuration/TokenConfig.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; + +namespace N2N.Api.Configuration +{ + public class TokenConfig + { + public const string ISSUER = "MyAuthServer"; // издатель токена + public const string AUDIENCE = "*"; // потребитель токена + const string KEY = "mysupersecret_secretkey!123"; // ключ для шифрации + public const int LIFETIME = 60; // время жизни токена (180 минут рекомендуемое) + public static SymmetricSecurityKey GetSymmetricSecurityKey() + { + return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KEY)); + } + } +} diff --git a/src/N2N.Api/Controllers/UserController.cs b/src/N2N.Api/Controllers/UserController.cs index 99f7676..5cc8647 100644 --- a/src/N2N.Api/Controllers/UserController.cs +++ b/src/N2N.Api/Controllers/UserController.cs @@ -3,12 +3,15 @@ using System.Linq; using System.Threading.Tasks; using IdentityServer4.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using N2N.Api.Filters; using N2N.Api.Services; using N2N.Core.Entities; using N2N.Infrastructure.Models; +using Newtonsoft.Json; namespace N2N.Api.Controllers { @@ -17,27 +20,55 @@ namespace N2N.Api.Controllers public class UserController : Controller { private N2NApiUserService _apiUserService; + private IAuthentificationService _authentificationService; - public UserController(N2NApiUserService apiUserService) + + public UserController(N2NApiUserService apiUserService, IAuthentificationService authentificationService) { + this._authentificationService = authentificationService; this._apiUserService = apiUserService; } + [N2NAutorizationFilter] + [HttpGet("/user/СheckUser")] + public async Task СheckUser() + { + var authHeader = HttpContext.Request.Headers["Authorization"]; + string welcome_message ="Welcome "+_authentificationService.GetNameUser(authHeader.ToString()); + return Json(welcome_message); + } + [HttpPost("/user/register")] - public async Task Register([FromBody] UserDataForm userData) + public async Task Register([FromBody] UserRegistrationFormDTO userRegistration) { - if (!userData.NickName.IsNullOrEmpty() && - !userData.Password.IsNullOrEmpty() && - !userData.Capcha.IsNullOrEmpty() ) + if (!userRegistration.NickName.IsNullOrEmpty() && + !userRegistration.Password.IsNullOrEmpty() && + !userRegistration.Capcha.IsNullOrEmpty() ) { - N2NUser user = new N2NUser() { NickName = userData.NickName }; - var result = await this._apiUserService.CreateUserAsync(user, userData.Password); + N2NUser user = new N2NUser() + { + Id = Guid.NewGuid(), + NickName = userRegistration.NickName + }; + + var result = await this._apiUserService.CreateUserAsync(user, userRegistration.Password); + if (!result.Success) { return BadRequest(result.Messages); } - return Ok(result.Messages); + + var identity = await this._authentificationService.GetIdentity(userRegistration.NickName, userRegistration.Password); + if (identity == null) + { + Response.StatusCode = 400; + await Response.WriteAsync("Invalid username or password."); + + } + var response = await this._authentificationService.GetTokenObject(identity, user.Id); + + return Ok(response); } else { @@ -46,5 +77,12 @@ public async Task Register([FromBody] UserDataForm userData) } + [HttpDelete("/user/LogOut")] + public void LogOut() + { + var authHeader = HttpContext.Request.Headers["Authorization"]; + this._authentificationService.DeleteToken(authHeader.ToString()); + } + } } diff --git a/src/N2N.Api/Filters/N2NAutorizationFilterAttribute.cs b/src/N2N.Api/Filters/N2NAutorizationFilterAttribute.cs new file mode 100644 index 0000000..dd88170 --- /dev/null +++ b/src/N2N.Api/Filters/N2NAutorizationFilterAttribute.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using N2N.Api.Services; +using N2N.Infrastructure.Models; +using N2N.Services; + +namespace N2N.Api.Filters +{ + public class N2NAutorizationFilterAttribute : ActionFilterAttribute + { + + + + public override void OnActionExecuting(ActionExecutingContext context) + { + + var service = context.HttpContext.RequestServices.GetService(); + + + ObjectResult result = new ObjectResult(""); + + var authHeader = context.HttpContext.Request.Headers["Authorization"]; + + + if (authHeader.IsNullOrEmpty()) + { + result = new ObjectResult("you do not have Authorization header"); + result.StatusCode = 401; + } + else + { + + var tokenValidationResult = service.ValidateTokenString(authHeader.ToString()); + + if (!tokenValidationResult.Success) + { + result = new ObjectResult(tokenValidationResult.Messages); + result.StatusCode = 401; + } + } + + context.Result = result; + } + + public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + + return base.OnActionExecutionAsync(context, next); + } + + public override void OnResultExecuted(ResultExecutedContext context) + { + base.OnResultExecuted(context); + } + + public override void OnResultExecuting(ResultExecutingContext context) + { + base.OnResultExecuting(context); + } + + public override Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + return base.OnResultExecutionAsync(context, next); + } + + + } +} diff --git a/src/N2N.Api/N2N.Api.csproj b/src/N2N.Api/N2N.Api.csproj index 5e4b00b..693a4a4 100644 --- a/src/N2N.Api/N2N.Api.csproj +++ b/src/N2N.Api/N2N.Api.csproj @@ -7,15 +7,16 @@ - - - - - - - - - + + + + + + + + + + diff --git a/src/N2N.Api/Services/AuthentificationService.cs b/src/N2N.Api/Services/AuthentificationService.cs new file mode 100644 index 0000000..5f2802b --- /dev/null +++ b/src/N2N.Api/Services/AuthentificationService.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using N2N.Api.Configuration; +using N2N.Core.Entities; +using N2N.Data.Repositories; +using N2N.Infrastructure.Models; + +namespace N2N.Api.Services +{ + public class AuthentificationService : IAuthentificationService + { + private UserManager _userManager; + private IRepository _tokenRepo; + + public AuthentificationService(UserManager userManager, IRepository tokenRepo) + { + this._userManager = userManager; + this._tokenRepo = tokenRepo; + } + + public string GetNameUser(string tokenString) + { + N2NIdentityUser user= new N2NIdentityUser(); + var tokenClaims = new List(); + var jwt = tokenString.Split(' ')[1]; + if (jwt != "") + { + tokenClaims = new JwtSecurityTokenHandler().ReadJwtToken(jwt).Claims.ToList(); + } + return tokenClaims[0].Value; + } + + public OperationResult ValidateTokenString(string tokenString) + { + var tokenClaims = new List(); + var result = new OperationResult(); + try + { + var jwt = tokenString.Split(' ')[1]; + + if (jwt != "") + { + tokenClaims = new JwtSecurityTokenHandler().ReadJwtToken(jwt).Claims.ToList(); + } + + if (_tokenRepo.Data.Any(x => x.Id.ToString() == tokenClaims.Find(y => y.Type == "Token Id").Value) && + _tokenRepo.Data.Any( z => z.TokenExpirationDate > DateTime.Now)) + { + result.Success = true; + } + else + { + result.Messages = new[] { "you do not have authorization token" }; + } + + } + catch (Exception exp) + { + result.Messages = new [] { exp.Message }; + } + + return result; + } + + public void DeleteToken(string tokenString) + { + var tokenClaims = new List(); + var jwt = tokenString.Split(' ')[1]; + if (jwt != "") + { + tokenClaims = new JwtSecurityTokenHandler().ReadJwtToken(jwt).Claims.ToList(); + _tokenRepo.Delete(_tokenRepo.Data.Where(x=>x.Id.ToString()== tokenClaims.Find(y => y.Type == "Token Id").Value)); + } + + } + + public async Task GetIdentity(string nickName, string password) + { + var access = await this._userManager.CheckPasswordAsync(this._userManager.Users.FirstOrDefault(x => x.UserName == nickName), password); + if (access) + { + + string roleforToken; + var user = await this._userManager.FindByNameAsync(nickName); + var role = await this._userManager.IsInRoleAsync(user, "Admin"); + var tokenId = Guid.NewGuid(); + _tokenRepo.Add(new N2NToken + { + Id = tokenId, + N2NUserId = user.N2NUserId, + TokenExpirationDate = DateTime.Now.AddMinutes(TokenConfig.LIFETIME) + }); + if (role != true) + { + roleforToken = "User"; + } + else + { + roleforToken = "Admin"; + } + if (user != null) + { + var claims = new List + { + new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName), + new Claim(ClaimsIdentity.DefaultRoleClaimType, roleforToken), + new Claim("Token Id",tokenId.ToString() ) + }; + ClaimsIdentity claimsIdentity = + new ClaimsIdentity(claims, "Token", ClaimsIdentity.DefaultNameClaimType, + ClaimsIdentity.DefaultRoleClaimType); + return claimsIdentity; + } + } + // если пользователя не найдено + return null; + } + + public async Task GetTokenObject(ClaimsIdentity identity,Guid userId) + { + var now = DateTime.Now; + // создаем JWT-токен + var jwt = new JwtSecurityToken( + issuer: TokenConfig.ISSUER, + audience: TokenConfig.AUDIENCE, + notBefore: now, + claims: identity.Claims, + expires: now.Add(TimeSpan.FromMinutes(TokenConfig.LIFETIME)), + signingCredentials: new SigningCredentials(TokenConfig.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)); + var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); + + var response = new + { + access_token = encodedJwt, + }; + return response; + } + } +} diff --git a/src/N2N.Api/Services/IAuthentificationService.cs b/src/N2N.Api/Services/IAuthentificationService.cs new file mode 100644 index 0000000..ec7342d --- /dev/null +++ b/src/N2N.Api/Services/IAuthentificationService.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using N2N.Infrastructure.Models; + +namespace N2N.Api.Services +{ + public interface IAuthentificationService + { + OperationResult ValidateTokenString(string tokenString); + + void DeleteToken(string tokenString); + Task GetTokenObject(ClaimsIdentity identity, Guid userId); + Task GetIdentity(string nickName, string password); + string GetNameUser(string tokenString); + } +} diff --git a/src/N2N.Api/Startup.cs b/src/N2N.Api/Startup.cs index c57e77f..aa4c9ea 100644 --- a/src/N2N.Api/Startup.cs +++ b/src/N2N.Api/Startup.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -11,7 +12,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using N2N.Api.Configuration; +using N2N.Api.Services; using N2N.Core.Entities; +using N2N.Data.Repositories; using N2N.Infrastructure.DataContext; using N2N.Infrastructure.Models; using SimpleInjector; @@ -36,6 +41,34 @@ public Startup(IConfiguration configuration, IHostingEnvironment env) // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { + + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.RequireHttpsMetadata = false; + options.TokenValidationParameters = new TokenValidationParameters + { + // укзывает, будет ли валидироваться издатель при валидации токена + ValidateIssuer = true, + // строка, представляющая издателя + ValidIssuer = TokenConfig.ISSUER, + + // будет ли валидироваться потребитель токена + ValidateAudience = true, + // установка потребителя токена + ValidAudience = TokenConfig.AUDIENCE, + // будет ли валидироваться время существования + ValidateLifetime = true, + + // установка ключа безопасности + IssuerSigningKey = TokenConfig.GetSymmetricSecurityKey(), + // валидация ключа безопасности + ValidateIssuerSigningKey = true, + ClockSkew = TimeSpan.Zero + + }; + }); + services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); @@ -45,6 +78,9 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores() .AddDefaultTokenProviders(); + services.AddTransient, DbRepository>(); + services.AddTransient(); + services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", @@ -66,16 +102,18 @@ public void ConfigureServices(IServiceCollection services) // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { - if (env.IsDevelopment()) - { + //if (env.IsDevelopment()) + //{ app.UseDeveloperExceptionPage(); - } + //} N2N.Api.Configuration.AppStart.UseMvcAndConfigureRoutes(app); N2N.Api.Configuration.AppStart.InitializeContainer(app, this.container); container.Verify(); + app.UseAuthentication(); + var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); diff --git a/src/N2N.Core/Entities/N2NToken.cs b/src/N2N.Core/Entities/N2NToken.cs new file mode 100644 index 0000000..558f929 --- /dev/null +++ b/src/N2N.Core/Entities/N2NToken.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace N2N.Core.Entities +{ + public class N2NToken + { + public Guid Id { get; set; } + public Guid N2NUserId { get; set; } + public DateTime TokenExpirationDate { get; set; } + + } +} diff --git a/src/N2N.Core/Entities/UserDataForm.cs b/src/N2N.Core/Entities/UserDataForm.cs deleted file mode 100644 index e66c2cc..0000000 --- a/src/N2N.Core/Entities/UserDataForm.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace N2N.Core.Entities -{ - public class UserDataForm - { - public string NickName { get; set; } - public string Password { get; set; } - public string Capcha { get; set; } - } -} diff --git a/src/N2N.Core/N2N.Core.csproj b/src/N2N.Core/N2N.Core.csproj index c7bca26..7b80865 100644 --- a/src/N2N.Core/N2N.Core.csproj +++ b/src/N2N.Core/N2N.Core.csproj @@ -40,11 +40,11 @@ + - diff --git a/src/N2N.Infrastructure/DataContext/N2NDataContext.cs b/src/N2N.Infrastructure/DataContext/N2NDataContext.cs index 87c6bb4..b2be702 100644 --- a/src/N2N.Infrastructure/DataContext/N2NDataContext.cs +++ b/src/N2N.Infrastructure/DataContext/N2NDataContext.cs @@ -18,6 +18,8 @@ public N2NDataContext(DbContextOptions options) } public DbSet N2NUsers { get; set; } + public DbSet N2NTokens { get; set; } + protected override void OnModelCreating(ModelBuilder builder) { diff --git a/src/N2N.Infrastructure/Migrations/20171201040445_Initi.Designer.cs b/src/N2N.Infrastructure/Migrations/20171201040445_Initi.Designer.cs new file mode 100644 index 0000000..f397171 --- /dev/null +++ b/src/N2N.Infrastructure/Migrations/20171201040445_Initi.Designer.cs @@ -0,0 +1,270 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using N2N.Infrastructure.DataContext; +using System; + +namespace N2N.Infrastructure.Migrations +{ + [DbContext(typeof(N2NDataContext))] + [Migration("20171201040445_Initi")] + partial class Initi + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.1-rtm-125") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("N2N.Core.Entities.N2NToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("N2NUserId"); + + b.Property("TokenExpirationDate"); + + b.HasKey("Id"); + + b.ToTable("N2NTokens"); + }); + + modelBuilder.Entity("N2N.Core.Entities.N2NUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Email"); + + b.Property("FirstName"); + + b.Property("LastName"); + + b.Property("NickName"); + + b.Property("PhoneNumber"); + + b.Property("Registration"); + + b.Property("UserPic"); + + b.HasKey("Id"); + + b.ToTable("N2NUsers"); + }); + + modelBuilder.Entity("N2N.Infrastructure.Models.N2NIdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("N2NUserId"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("N2N.Infrastructure.Models.N2NIdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("N2N.Infrastructure.Models.N2NIdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("N2N.Infrastructure.Models.N2NIdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("N2N.Infrastructure.Models.N2NIdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/N2N.Infrastructure/Migrations/20171201040445_Initi.cs b/src/N2N.Infrastructure/Migrations/20171201040445_Initi.cs new file mode 100644 index 0000000..8b40c6b --- /dev/null +++ b/src/N2N.Infrastructure/Migrations/20171201040445_Initi.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace N2N.Infrastructure.Migrations +{ + public partial class Initi : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "N2NTokens", + columns: table => new + { + Id = table.Column(nullable: false), + N2NUserId = table.Column(nullable: false), + TokenExpirationDate = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_N2NTokens", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "N2NTokens"); + } + } +} diff --git a/src/N2N.Infrastructure/Migrations/N2NDataContextModelSnapshot.cs b/src/N2N.Infrastructure/Migrations/N2NDataContextModelSnapshot.cs index 5de902e..5b0c8c7 100644 --- a/src/N2N.Infrastructure/Migrations/N2NDataContextModelSnapshot.cs +++ b/src/N2N.Infrastructure/Migrations/N2NDataContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452") + .HasAnnotation("ProductVersion", "2.0.1-rtm-125") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => @@ -128,6 +128,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens"); }); + modelBuilder.Entity("N2N.Core.Entities.N2NToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("N2NUserId"); + + b.Property("TokenExpirationDate"); + + b.HasKey("Id"); + + b.ToTable("N2NTokens"); + }); + modelBuilder.Entity("N2N.Core.Entities.N2NUser", b => { b.Property("Id") diff --git a/src/N2N.Infrastructure/Models/UserRegistrationFormDTO.cs b/src/N2N.Infrastructure/Models/UserRegistrationFormDTO.cs new file mode 100644 index 0000000..f601dd6 --- /dev/null +++ b/src/N2N.Infrastructure/Models/UserRegistrationFormDTO.cs @@ -0,0 +1,9 @@ +namespace N2N.Infrastructure.Models +{ + public class UserRegistrationFormDTO + { + public string NickName { get; set; } + public string Password { get; set; } + public string Capcha { get; set; } + } +} diff --git a/src/N2N.Infrastructure/N2N.Infrasturcture.csproj b/src/N2N.Infrastructure/N2N.Infrasturcture.csproj index ed88902..c3ef08f 100644 --- a/src/N2N.Infrastructure/N2N.Infrasturcture.csproj +++ b/src/N2N.Infrastructure/N2N.Infrasturcture.csproj @@ -30,68 +30,68 @@ 4 - - ..\..\packages\Microsoft.AspNetCore.Authentication.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.dll + + ..\..\packages\Microsoft.AspNetCore.Authentication.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.dll - - ..\..\packages\Microsoft.AspNetCore.Authentication.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.Abstractions.dll + + ..\..\packages\Microsoft.AspNetCore.Authentication.Abstractions.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.Abstractions.dll - - ..\..\packages\Microsoft.AspNetCore.Authentication.Cookies.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.Cookies.dll + + ..\..\packages\Microsoft.AspNetCore.Authentication.Cookies.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.Cookies.dll - - ..\..\packages\Microsoft.AspNetCore.Authentication.Core.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.Core.dll + + ..\..\packages\Microsoft.AspNetCore.Authentication.Core.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Authentication.Core.dll - - ..\..\packages\Microsoft.AspNetCore.Cryptography.Internal.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Cryptography.Internal.dll + + ..\..\packages\Microsoft.AspNetCore.Cryptography.Internal.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Cryptography.Internal.dll - - ..\..\packages\Microsoft.AspNetCore.Cryptography.KeyDerivation.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Cryptography.KeyDerivation.dll + + ..\..\packages\Microsoft.AspNetCore.Cryptography.KeyDerivation.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Cryptography.KeyDerivation.dll - - ..\..\packages\Microsoft.AspNetCore.DataProtection.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.DataProtection.dll + + ..\..\packages\Microsoft.AspNetCore.DataProtection.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.DataProtection.dll - - ..\..\packages\Microsoft.AspNetCore.DataProtection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.DataProtection.Abstractions.dll + + ..\..\packages\Microsoft.AspNetCore.DataProtection.Abstractions.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.DataProtection.Abstractions.dll - - ..\..\packages\Microsoft.AspNetCore.Hosting.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Hosting.Abstractions.dll + + ..\..\packages\Microsoft.AspNetCore.Hosting.Abstractions.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Hosting.Abstractions.dll - - ..\..\packages\Microsoft.AspNetCore.Hosting.Server.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Hosting.Server.Abstractions.dll + + ..\..\packages\Microsoft.AspNetCore.Hosting.Server.Abstractions.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Hosting.Server.Abstractions.dll - - ..\..\packages\Microsoft.AspNetCore.Http.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.dll + + ..\..\packages\Microsoft.AspNetCore.Http.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Http.dll - - ..\..\packages\Microsoft.AspNetCore.Http.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Abstractions.dll + + ..\..\packages\Microsoft.AspNetCore.Http.Abstractions.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Http.Abstractions.dll - - ..\..\packages\Microsoft.AspNetCore.Http.Extensions.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Extensions.dll + + ..\..\packages\Microsoft.AspNetCore.Http.Extensions.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Http.Extensions.dll - - ..\..\packages\Microsoft.AspNetCore.Http.Features.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Features.dll + + ..\..\packages\Microsoft.AspNetCore.Http.Features.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Http.Features.dll - - ..\..\packages\Microsoft.AspNetCore.Identity.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Identity.dll + + ..\..\packages\Microsoft.AspNetCore.Identity.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Identity.dll - - ..\..\packages\Microsoft.AspNetCore.Identity.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll + + ..\..\packages\Microsoft.AspNetCore.Identity.EntityFrameworkCore.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll - - ..\..\packages\Microsoft.AspNetCore.WebUtilities.2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.WebUtilities.dll + + ..\..\packages\Microsoft.AspNetCore.WebUtilities.2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.WebUtilities.dll - - ..\..\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll + + ..\..\packages\Microsoft.EntityFrameworkCore.2.0.1\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll - - ..\..\packages\Microsoft.EntityFrameworkCore.Design.2.0.0\lib\net461\Microsoft.EntityFrameworkCore.Design.dll + + ..\..\packages\Microsoft.EntityFrameworkCore.Design.2.0.1\lib\net461\Microsoft.EntityFrameworkCore.Design.dll - - ..\..\packages\Microsoft.EntityFrameworkCore.Relational.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll + + ..\..\packages\Microsoft.EntityFrameworkCore.Relational.2.0.1\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll - - ..\..\packages\Microsoft.EntityFrameworkCore.SqlServer.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.SqlServer.dll + + ..\..\packages\Microsoft.EntityFrameworkCore.SqlServer.2.0.1\lib\netstandard2.0\Microsoft.EntityFrameworkCore.SqlServer.dll ..\..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll @@ -111,14 +111,14 @@ ..\..\packages\Microsoft.Extensions.FileProviders.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.FileProviders.Abstractions.dll - - ..\..\packages\Microsoft.Extensions.Hosting.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Hosting.Abstractions.dll + + ..\..\packages\Microsoft.Extensions.Hosting.Abstractions.2.0.1\lib\netstandard2.0\Microsoft.Extensions.Hosting.Abstractions.dll - - ..\..\packages\Microsoft.Extensions.Identity.Core.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Identity.Core.dll + + ..\..\packages\Microsoft.Extensions.Identity.Core.2.0.1\lib\netstandard2.0\Microsoft.Extensions.Identity.Core.dll - - ..\..\packages\Microsoft.Extensions.Identity.Stores.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Identity.Stores.dll + + ..\..\packages\Microsoft.Extensions.Identity.Stores.2.0.1\lib\netstandard2.0\Microsoft.Extensions.Identity.Stores.dll ..\..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll @@ -138,18 +138,18 @@ ..\..\packages\Microsoft.Extensions.WebEncoders.2.0.0\lib\netstandard2.0\Microsoft.Extensions.WebEncoders.dll - - ..\..\packages\Microsoft.Net.Http.Headers.2.0.0\lib\netstandard2.0\Microsoft.Net.Http.Headers.dll + + ..\..\packages\Microsoft.Net.Http.Headers.2.0.1\lib\netstandard2.0\Microsoft.Net.Http.Headers.dll - - ..\..\packages\Microsoft.Win32.Registry.4.4.0\lib\net461\Microsoft.Win32.Registry.dll + + ..\..\packages\Microsoft.Win32.Registry.4.5.0-preview1-25914-04\lib\net461\Microsoft.Win32.Registry.dll - - ..\..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll + + ..\..\packages\Remotion.Linq.2.2.0-alpha-004\lib\net45\Remotion.Linq.dll - - ..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + ..\..\packages\System.Buffers.4.5.0-preview1-25914-04\lib\netstandard2.0\System.Buffers.dll ..\..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll @@ -160,51 +160,58 @@ + ..\..\packages\System.Data.SqlClient.4.4.0\lib\net461\System.Data.SqlClient.dll ..\..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + ..\..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll - ..\..\packages\System.Linq.4.1.0\lib\net463\System.Linq.dll + ..\..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll True - ..\..\packages\System.Linq.Expressions.4.1.0\lib\net463\System.Linq.Expressions.dll + ..\..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll True + - ..\..\packages\System.Reflection.4.1.0\lib\net462\System.Reflection.dll + ..\..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll True - ..\..\packages\System.Runtime.4.1.0\lib\net462\System.Runtime.dll + ..\..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll True ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - ..\..\packages\System.Runtime.Extensions.4.1.0\lib\net462\System.Runtime.Extensions.dll + ..\..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll True - - ..\..\packages\System.Security.AccessControl.4.4.0\lib\net461\System.Security.AccessControl.dll + + ..\..\packages\System.Security.AccessControl.4.5.0-preview1-25914-04\lib\net461\System.Security.AccessControl.dll - - ..\..\packages\System.Security.Cryptography.Xml.4.4.0\lib\net461\System.Security.Cryptography.Xml.dll + + ..\..\packages\System.Security.Cryptography.Xml.4.5.0-preview1-25914-04\lib\net461\System.Security.Cryptography.Xml.dll - - ..\..\packages\System.Security.Principal.Windows.4.4.0\lib\net461\System.Security.Principal.Windows.dll + + ..\..\packages\System.Security.Permissions.4.5.0-preview1-25914-04\lib\net461\System.Security.Permissions.dll + + + ..\..\packages\System.Security.Principal.Windows.4.5.0-preview1-25914-04\lib\net461\System.Security.Principal.Windows.dll ..\..\packages\System.Text.Encodings.Web.4.4.0\lib\netstandard2.0\System.Text.Encodings.Web.dll + @@ -215,23 +222,31 @@ - + 20171030103621_init.cs - + 20171127042133_Initial.cs + + + 20171201040445_Initi.cs + + - + + + Designer + diff --git a/src/N2N.Infrastructure/app.config b/src/N2N.Infrastructure/app.config new file mode 100644 index 0000000..ba16d62 --- /dev/null +++ b/src/N2N.Infrastructure/app.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/N2N.Infrastructure/packages.config b/src/N2N.Infrastructure/packages.config index 1b903b9..95c3729 100644 --- a/src/N2N.Infrastructure/packages.config +++ b/src/N2N.Infrastructure/packages.config @@ -1,66 +1,67 @@  - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - + + + - - - - - + + + + + - + - - - - - - - + + + + + + + - - - - + + + + + - + \ No newline at end of file diff --git a/src/N2N.Services/N2N.Services.csproj b/src/N2N.Services/N2N.Services.csproj index 2435170..866732f 100644 --- a/src/N2N.Services/N2N.Services.csproj +++ b/src/N2N.Services/N2N.Services.csproj @@ -56,6 +56,8 @@ N2N.Infrasturcture - + + + \ No newline at end of file diff --git a/src/N2N.Services/app.config b/src/N2N.Services/app.config new file mode 100644 index 0000000..ba16d62 --- /dev/null +++ b/src/N2N.Services/app.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/N2N.Utilities/N2N.Utilities.csproj b/src/N2N.Utilities/N2N.Utilities.csproj index ea17934..ae14ed2 100644 --- a/src/N2N.Utilities/N2N.Utilities.csproj +++ b/src/N2N.Utilities/N2N.Utilities.csproj @@ -44,6 +44,7 @@ + \ No newline at end of file diff --git a/src/N2N.Web/N2N.Web.csproj b/src/N2N.Web/N2N.Web.csproj index fc9726d..e21d623 100644 --- a/src/N2N.Web/N2N.Web.csproj +++ b/src/N2N.Web/N2N.Web.csproj @@ -13,9 +13,9 @@ - - - + + + diff --git a/src/N2N.Web/src/app/app.module.ts b/src/N2N.Web/src/app/app.module.ts index 94aad66..6e81d7b 100644 --- a/src/N2N.Web/src/app/app.module.ts +++ b/src/N2N.Web/src/app/app.module.ts @@ -13,6 +13,8 @@ import {LogInComponent} from './login/login.component'; import {RegistrationComponent} from './registration/registration.component'; import {UserService} from './userService'; +import {StoreHeaders} from './storeHeaders' +import {StoreLinks} from './storeLinks' import { AppRoutingModule } from './app-routing.module'; @@ -40,10 +42,15 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; BrowserAnimationsModule, FormsModule, HttpModule, - ReactiveFormsModule + ReactiveFormsModule, + ], entryComponents: [LogInComponent,RegistrationComponent], - providers: [UserService], + providers: [ + UserService, + StoreHeaders, + StoreLinks + ], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/src/N2N.Web/src/app/navmenu/navmenu.component.html b/src/N2N.Web/src/app/navmenu/navmenu.component.html index 5063481..837f8d9 100644 --- a/src/N2N.Web/src/app/navmenu/navmenu.component.html +++ b/src/N2N.Web/src/app/navmenu/navmenu.component.html @@ -5,6 +5,8 @@ + + Anonymous