From 258508e1b82b93a85b40e441e10abb72808864c6 Mon Sep 17 00:00:00 2001 From: LICH010 Date: Fri, 22 Dec 2017 11:16:46 +0600 Subject: [PATCH] n2n/9 Add JWT authorization for N2N.Api project and n2n/10 Add User registration feature --- src/N2N.Api/Configuration/AppStart.cs | 1 + src/N2N.Api/Configuration/IConfig.cs | 14 + .../Configuration/RefreshTokenConfig.cs | 24 ++ src/N2N.Api/Configuration/TokenConfig.cs | 16 +- src/N2N.Api/Controllers/UserController.cs | 49 +++- src/N2N.Api/N2N.Api.csproj | 5 + .../Services/AuthentificationService.cs | 102 +++++-- .../Services/IAuthentificationService.cs | 3 +- src/N2N.Api/Startup.cs | 6 +- src/N2N.Core/Entities/N2NRefreshToken.cs | 15 + src/N2N.Core/Entities/N2NToken.cs | 1 + src/N2N.Core/N2N.Core.csproj | 1 + .../DataContext/N2NDataContext.cs | 1 + src/N2N.Web/src/app/login/login.component.ts | 80 ++++-- .../src/app/navmenu/navmenu.component.spec.ts | 48 ++++ .../src/app/navmenu/navmenu.component.ts | 6 +- .../registration/registration.component.ts | 4 +- src/N2N.Web/src/app/storeHeaders.ts | 1 - src/N2N.Web/src/app/storeLinks.ts | 3 +- src/N2N.Web/src/app/userService.spec.ts | 270 ++++++++++++++++++ src/N2N.Web/src/app/userService.ts | 25 +- 21 files changed, 593 insertions(+), 82 deletions(-) create mode 100644 src/N2N.Api/Configuration/IConfig.cs create mode 100644 src/N2N.Api/Configuration/RefreshTokenConfig.cs create mode 100644 src/N2N.Core/Entities/N2NRefreshToken.cs create mode 100644 src/N2N.Web/src/app/navmenu/navmenu.component.spec.ts create mode 100644 src/N2N.Web/src/app/userService.spec.ts diff --git a/src/N2N.Api/Configuration/AppStart.cs b/src/N2N.Api/Configuration/AppStart.cs index 87be828..4e3db97 100644 --- a/src/N2N.Api/Configuration/AppStart.cs +++ b/src/N2N.Api/Configuration/AppStart.cs @@ -76,6 +76,7 @@ internal static void InitializeContainer(IApplicationBuilder app, Container cont // Dependencies container.Register, DbRepository>(); container.Register, DbRepository>(); + container.Register, DbRepository>(); container.Register(); container.Register(); container.Register(); diff --git a/src/N2N.Api/Configuration/IConfig.cs b/src/N2N.Api/Configuration/IConfig.cs new file mode 100644 index 0000000..0644e30 --- /dev/null +++ b/src/N2N.Api/Configuration/IConfig.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace N2N.Api.Configuration +{ + interface IConfig + { + string ISSUER { get; set; } + string AUDIENCE { get; set; } + int LIFETIME { get; set; } + } +} diff --git a/src/N2N.Api/Configuration/RefreshTokenConfig.cs b/src/N2N.Api/Configuration/RefreshTokenConfig.cs new file mode 100644 index 0000000..2c82002 --- /dev/null +++ b/src/N2N.Api/Configuration/RefreshTokenConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IdentityServer4.Test; +using Microsoft.IdentityModel.Tokens; + +namespace N2N.Api.Configuration +{ + public class RefreshTokenConfig : IConfig + { + public string ISSUER { get; set; } + public string AUDIENCE { get; set; } + public int LIFETIME { get; set; } + + public RefreshTokenConfig() + { + this.ISSUER = "MyAuthServer"; // издатель токена + this.AUDIENCE = "*"; + this.LIFETIME = 30; // время жизни токена (30 дней рекомендуемое) + } + } +} diff --git a/src/N2N.Api/Configuration/TokenConfig.cs b/src/N2N.Api/Configuration/TokenConfig.cs index 3e0d105..bd1e911 100644 --- a/src/N2N.Api/Configuration/TokenConfig.cs +++ b/src/N2N.Api/Configuration/TokenConfig.cs @@ -7,12 +7,20 @@ namespace N2N.Api.Configuration { - public class TokenConfig + public class TokenConfig :IConfig { - public const string ISSUER = "MyAuthServer"; // издатель токена - public const string AUDIENCE = "*"; // потребитель токена + public string ISSUER { get; set; } + public string AUDIENCE { get; set; } + public int LIFETIME { get; set; } + + public TokenConfig() + { + this.ISSUER = "MyAuthServer"; // издатель токена + this.AUDIENCE = "*"; + this.LIFETIME = LIFETIME = 60; // время жизни токена (180 минут рекомендуемое) + } 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 5cc8647..376c4ac 100644 --- a/src/N2N.Api/Controllers/UserController.cs +++ b/src/N2N.Api/Controllers/UserController.cs @@ -29,13 +29,43 @@ public UserController(N2NApiUserService apiUserService, IAuthentificationService this._apiUserService = apiUserService; } - [N2NAutorizationFilter] - [HttpGet("/user/СheckUser")] + //[N2NAutorizationFilter] + [HttpGet("/user")] public async Task СheckUser() { var authHeader = HttpContext.Request.Headers["Authorization"]; - string welcome_message ="Welcome "+_authentificationService.GetNameUser(authHeader.ToString()); - return Json(welcome_message); + if (authHeader!="Bearer") + { + string welcome_message = "Welcome " + _authentificationService.GetNameUser(authHeader.ToString()); + return Json(welcome_message); + } + return Json("You not authentification"); + } + + + [HttpPost("/user/logIn")] + public async Task LogIn([FromBody] UserRegistrationFormDTO userRegistration) + { + + if (!userRegistration.NickName.IsNullOrEmpty() && + !userRegistration.Password.IsNullOrEmpty() && + !userRegistration.Capcha.IsNullOrEmpty()) + { + var response = + await _authentificationService.Authentification(userRegistration.NickName, + userRegistration.Password); + + if (response.ToString() == new { }.ToString()) + { + return BadRequest("Invalid username or password."); + + } + return Ok(response); + } + else + { + return BadRequest("fill in all the fields"); + } } [HttpPost("/user/register")] @@ -59,15 +89,14 @@ public async Task Register([FromBody] UserRegistrationFormDTO use return BadRequest(result.Messages); } - var identity = await this._authentificationService.GetIdentity(userRegistration.NickName, userRegistration.Password); - if (identity == null) + var response =await _authentificationService.Authentification(userRegistration.NickName, userRegistration.Password); + + if (response.ToString() == new { }.ToString()) { - Response.StatusCode = 400; - await Response.WriteAsync("Invalid username or password."); + return BadRequest("Invalid username or password."); } - var response = await this._authentificationService.GetTokenObject(identity, user.Id); - + return Ok(response); } else diff --git a/src/N2N.Api/N2N.Api.csproj b/src/N2N.Api/N2N.Api.csproj index 693a4a4..58e06bd 100644 --- a/src/N2N.Api/N2N.Api.csproj +++ b/src/N2N.Api/N2N.Api.csproj @@ -11,6 +11,7 @@ + @@ -29,4 +30,8 @@ + + + + diff --git a/src/N2N.Api/Services/AuthentificationService.cs b/src/N2N.Api/Services/AuthentificationService.cs index 5f2802b..ab59fa1 100644 --- a/src/N2N.Api/Services/AuthentificationService.cs +++ b/src/N2N.Api/Services/AuthentificationService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using IdentityServer4.Test; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; @@ -18,11 +19,14 @@ public class AuthentificationService : IAuthentificationService { private UserManager _userManager; private IRepository _tokenRepo; + private IRepository _RefreshTokenRepo; + - public AuthentificationService(UserManager userManager, IRepository tokenRepo) + public AuthentificationService(UserManager userManager, IRepository tokenRepo, IRepository RefreshTokenRepo) { this._userManager = userManager; this._tokenRepo = tokenRepo; + this._RefreshTokenRepo = RefreshTokenRepo; } public string GetNameUser(string tokenString) @@ -81,7 +85,65 @@ public void DeleteToken(string tokenString) } - public async Task GetIdentity(string nickName, string password) + public async Task Authentification(string nickName, string password) + { + object response= new { }; + var access_token = await GetToken(nickName,password); + if (access_token !="") + { + var refresh_token = await GetRefreshToken(nickName, password); + response= new + { + access_token = access_token, + refresh_token = refresh_token + }; + } + return response; + } + + public async Task GetToken(string nickName, string password) + { + var Token=""; + var tokenConfig = new TokenConfig(); + var lifeTime = TimeSpan.FromMinutes(tokenConfig.LIFETIME); + var user = await this._userManager.FindByNameAsync(nickName); + var tokenId = Guid.NewGuid(); + + var claimIdentity = await GetIdentity(nickName, password, tokenId); + if (claimIdentity != null) + { + _tokenRepo.Add(new N2NToken + { + Id = tokenId, + N2NUserId = user.N2NUserId, + TokenExpirationDate = DateTime.Now.AddMinutes(tokenConfig.LIFETIME) + }); + Token = await GetTokenObject(claimIdentity, user.N2NUserId, tokenConfig.ISSUER, + tokenConfig.AUDIENCE, + lifeTime); + } + return Token; + } + + public async Task GetRefreshToken(string nickName, string password) + { + var tokenConfig = new RefreshTokenConfig(); + var lifeTime = TimeSpan.FromDays(tokenConfig.LIFETIME); + var user = await this._userManager.FindByNameAsync(nickName); + var tokenId = Guid.NewGuid(); + _RefreshTokenRepo.Add(new N2NRefreshToken() + { + Id = tokenId, + N2NUserId = user.N2NUserId, + RefreshTokenExpirationDate = DateTime.Now.AddDays(tokenConfig.LIFETIME) + }); + var claimIdentity = await GetIdentity(nickName, password, tokenId); + var refreshToken =await GetTokenObject(claimIdentity, user.N2NUserId, tokenConfig.ISSUER, tokenConfig.AUDIENCE, + lifeTime); + return refreshToken; + } + + public async Task GetIdentity(string nickName, string password,Guid tokenId) { var access = await this._userManager.CheckPasswordAsync(this._userManager.Users.FirstOrDefault(x => x.UserName == nickName), password); if (access) @@ -90,13 +152,6 @@ public async Task GetIdentity(string nickName, string password) 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"; @@ -123,23 +178,24 @@ public async Task GetIdentity(string nickName, string password) return null; } - public async Task GetTokenObject(ClaimsIdentity identity,Guid userId) + public async Task GetTokenObject(ClaimsIdentity identity,Guid userId, string issuer , string audience, TimeSpan lifetime) { + + string response=""; + 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 jwt = new JwtSecurityToken( + issuer: issuer, + audience: audience, + notBefore: now, + claims: identity.Claims, + expires: now.Add(lifetime), + signingCredentials: new SigningCredentials(TokenConfig.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)); + var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); + + response = encodedJwt; + - var response = new - { - access_token = encodedJwt, - }; return response; } } diff --git a/src/N2N.Api/Services/IAuthentificationService.cs b/src/N2N.Api/Services/IAuthentificationService.cs index ec7342d..fba8a3e 100644 --- a/src/N2N.Api/Services/IAuthentificationService.cs +++ b/src/N2N.Api/Services/IAuthentificationService.cs @@ -13,8 +13,7 @@ public interface IAuthentificationService OperationResult ValidateTokenString(string tokenString); void DeleteToken(string tokenString); - Task GetTokenObject(ClaimsIdentity identity, Guid userId); - Task GetIdentity(string nickName, string password); + Task Authentification(string nickName, string password); string GetNameUser(string tokenString); } } diff --git a/src/N2N.Api/Startup.cs b/src/N2N.Api/Startup.cs index aa4c9ea..fb15a74 100644 --- a/src/N2N.Api/Startup.cs +++ b/src/N2N.Api/Startup.cs @@ -41,6 +41,7 @@ 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) { + var tokenConfig = new TokenConfig(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => @@ -51,12 +52,12 @@ public void ConfigureServices(IServiceCollection services) // укзывает, будет ли валидироваться издатель при валидации токена ValidateIssuer = true, // строка, представляющая издателя - ValidIssuer = TokenConfig.ISSUER, + ValidIssuer = tokenConfig.ISSUER, // будет ли валидироваться потребитель токена ValidateAudience = true, // установка потребителя токена - ValidAudience = TokenConfig.AUDIENCE, + ValidAudience = tokenConfig.AUDIENCE, // будет ли валидироваться время существования ValidateLifetime = true, @@ -78,6 +79,7 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores() .AddDefaultTokenProviders(); + services.AddTransient, DbRepository>(); services.AddTransient, DbRepository>(); services.AddTransient(); diff --git a/src/N2N.Core/Entities/N2NRefreshToken.cs b/src/N2N.Core/Entities/N2NRefreshToken.cs new file mode 100644 index 0000000..8aee42d --- /dev/null +++ b/src/N2N.Core/Entities/N2NRefreshToken.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace N2N.Core.Entities +{ + public class N2NRefreshToken + { + public Guid Id { get; set; } + public Guid N2NUserId { get; set; } + public DateTime RefreshTokenExpirationDate { get; set; } + } +} diff --git a/src/N2N.Core/Entities/N2NToken.cs b/src/N2N.Core/Entities/N2NToken.cs index 558f929..c66e4a0 100644 --- a/src/N2N.Core/Entities/N2NToken.cs +++ b/src/N2N.Core/Entities/N2NToken.cs @@ -11,6 +11,7 @@ public class N2NToken public Guid Id { get; set; } public Guid N2NUserId { get; set; } public DateTime TokenExpirationDate { get; set; } + public Guid IdRefreshToken { get; set; } } } diff --git a/src/N2N.Core/N2N.Core.csproj b/src/N2N.Core/N2N.Core.csproj index 7b80865..20cb25f 100644 --- a/src/N2N.Core/N2N.Core.csproj +++ b/src/N2N.Core/N2N.Core.csproj @@ -40,6 +40,7 @@ + diff --git a/src/N2N.Infrastructure/DataContext/N2NDataContext.cs b/src/N2N.Infrastructure/DataContext/N2NDataContext.cs index b2be702..03692c3 100644 --- a/src/N2N.Infrastructure/DataContext/N2NDataContext.cs +++ b/src/N2N.Infrastructure/DataContext/N2NDataContext.cs @@ -19,6 +19,7 @@ public N2NDataContext(DbContextOptions options) public DbSet N2NUsers { get; set; } public DbSet N2NTokens { get; set; } + public DbSet N2NRefreshTokens { get; set; } protected override void OnModelCreating(ModelBuilder builder) diff --git a/src/N2N.Web/src/app/login/login.component.ts b/src/N2N.Web/src/app/login/login.component.ts index 02d9414..2c37cbe 100644 --- a/src/N2N.Web/src/app/login/login.component.ts +++ b/src/N2N.Web/src/app/login/login.component.ts @@ -1,35 +1,55 @@ -import {Component, Inject} from '@angular/core'; -import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material'; - -import {RegistrationComponent} from '../registration/registration.component'; +import { Component, Inject } from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { UserService } from '../userService' +import { StoreHeaders } from '../storeHeaders' +import { RegistrationComponent } from '../registration/registration.component'; @Component({ - selector: 'login', - templateUrl: 'login.component.html', - styleUrls: ['./login.component.css'] - }) - export class LogInComponent { - - constructor( - public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) { } - - sendDataLogIn(nickname:string,password:string,capcha:string):void - { - - console.log(nickname,password); - } - - onNoClick(): void { - this.dialogRef.close(); + selector: 'login', + templateUrl: 'login.component.html', + styleUrls: ['./login.component.css'] +}) +export class LogInComponent { + + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + private _userService: UserService, + private _storeHeaders: StoreHeaders, + @Inject(MAT_DIALOG_DATA) public data: any) { } + + sendDataLogIn(nickname: string, password: string, capcha: string): void { + var nickname: string = nickname.trim(); + var password: string = password.trim(); + var capcha: string = capcha.trim(); + + if (nickname != "" && password != "" && capcha != "") { + + this._userService.logIn(nickname, password, capcha) + .then(data => { + localStorage.setItem("Token", JSON.parse(data._body).access_token); + this._storeHeaders.refrechJsonAndTokenHeaders(); + alert(JSON.parse(data._body).access_token); + }) + .catch(data => { + alert(data._body); + }); + } + else { + alert("Не все поля заполнены"); } + } + + + onNoClick(): void { + this.dialogRef.close(); + } + + openDialog(): void { + this.dialogRef.close(); + let dialogRef = this.dialog.open(RegistrationComponent, { + width: '350px', + + }); - openDialog(): void { - this.dialogRef.close(); - let dialogRef = this.dialog.open(RegistrationComponent, { - width: '350px', - - }); - } } \ No newline at end of file diff --git a/src/N2N.Web/src/app/navmenu/navmenu.component.spec.ts b/src/N2N.Web/src/app/navmenu/navmenu.component.spec.ts new file mode 100644 index 0000000..210a36b --- /dev/null +++ b/src/N2N.Web/src/app/navmenu/navmenu.component.spec.ts @@ -0,0 +1,48 @@ +import { async, ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; +import { NavMenuComponent } from './navmenu.component'; +import { RouterTestingModule } from '@angular/router/testing' +import { MatDialogModule } from '@angular/material'; + +import { HttpModule } from '@angular/http'; + +import {StoreHeaders} from '../storeHeaders'; +import {StoreLinks} from '../storeLinks'; +import {UserService} from '../userService'; + +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +describe('NavMenuComponent', () => { + let comp: NavMenuComponent; + + let fixture: ComponentFixture; + let heroEl: DebugElement; + beforeEach(async(() => { + fixture = TestBed.createComponent(NavMenuComponent); + comp = fixture.componentInstance; + heroEl = fixture.debugElement.query(By.css('.main-nav')); + TestBed.configureTestingModule({ + declarations: [ + + NavMenuComponent + ], + imports: [ + RouterTestingModule, + MatDialogModule, + HttpModule + ], + providers:[ + UserService, + StoreHeaders, + StoreLinks + ] + }).compileComponents(); + })); + + + // it('should create the app', async(() => { + // const fixture = TestBed.createComponent(NavMenuComponent); + // const app = fixture.debugElement.componentInstance; + // expect(app).toBeTruthy(); + // })); +}); \ No newline at end of file diff --git a/src/N2N.Web/src/app/navmenu/navmenu.component.ts b/src/N2N.Web/src/app/navmenu/navmenu.component.ts index d1a2d7b..0a32d3b 100644 --- a/src/N2N.Web/src/app/navmenu/navmenu.component.ts +++ b/src/N2N.Web/src/app/navmenu/navmenu.component.ts @@ -3,6 +3,7 @@ import { MatDialog } from '@angular/material'; import {LogInComponent} from '../login/login.component'; import {UserService} from '../userService'; +import {StoreHeaders} from '../storeHeaders' @Component({ selector: 'nav-menu', @@ -13,12 +14,13 @@ export class NavMenuComponent { constructor( public dialog: MatDialog, - public userService:UserService + public userService:UserService, + private _storeHeaders:StoreHeaders ) {} logout():void{ - this.userService.logOut(); + this.userService.logOut().then(data =>{debugger; this._storeHeaders.refrechJsonAndTokenHeaders()}); } checkout():void{ diff --git a/src/N2N.Web/src/app/registration/registration.component.ts b/src/N2N.Web/src/app/registration/registration.component.ts index f25a290..ede3ea0 100644 --- a/src/N2N.Web/src/app/registration/registration.component.ts +++ b/src/N2N.Web/src/app/registration/registration.component.ts @@ -33,14 +33,12 @@ export class RegistrationComponent { this._userService.sendUserDataForRegistration(nickname, password, capcha) .then(data => { - debugger; - localStorage.setItem("Token", JSON.parse(data._body).access_token); this._storeHeaders.refrechJsonAndTokenHeaders(); alert(JSON.parse(data._body).access_token); }) - .catch(data => { + .catch(data => { debugger alert(data._body); }); } diff --git a/src/N2N.Web/src/app/storeHeaders.ts b/src/N2N.Web/src/app/storeHeaders.ts index 1823d44..7721d52 100644 --- a/src/N2N.Web/src/app/storeHeaders.ts +++ b/src/N2N.Web/src/app/storeHeaders.ts @@ -14,7 +14,6 @@ export class StoreHeaders { }); public refrechJsonAndTokenHeaders(){ - debugger; this.jsonAndTokenHeaders=new Headers({ 'Content-Type': 'application/json charset=utf-8', "Authorization": 'Bearer ' + localStorage.getItem('Token') diff --git a/src/N2N.Web/src/app/storeLinks.ts b/src/N2N.Web/src/app/storeLinks.ts index 2f98f19..ccc0f27 100644 --- a/src/N2N.Web/src/app/storeLinks.ts +++ b/src/N2N.Web/src/app/storeLinks.ts @@ -5,6 +5,7 @@ import { Injectable } from '@angular/core'; export class StoreLinks { public registerUrl = 'http://localhost:63354/User/register'; public logoutUrl = 'http://localhost:63354/User/LogOut'; - public СheckUserUrl = 'http://localhost:63354/User/СheckUser'; + public СheckUserUrl = 'http://localhost:63354/User'; + public logInUrl='http://localhost:63354/User/LogIn' } \ No newline at end of file diff --git a/src/N2N.Web/src/app/userService.spec.ts b/src/N2N.Web/src/app/userService.spec.ts new file mode 100644 index 0000000..f77e0e3 --- /dev/null +++ b/src/N2N.Web/src/app/userService.spec.ts @@ -0,0 +1,270 @@ +import { async, inject, TestBed } from '@angular/core/testing'; + +import { + MockBackend, + MockConnection +} from '@angular/http/testing'; + +import { RegistrationComponent } from './registration/registration.component' +import { + HttpModule, + Http, + Headers, + XHRBackend, + ResponseOptions, + Response, +} from '@angular/http'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/toPromise'; + +import { StoreHeaders } from './storeHeaders'; +import { StoreLinks } from './storeLinks'; +import { UserService } from './userService'; + +describe('UserService', () => { + // Mock http response + + let mockResponse = [ + { + 'access_token': '', + 'refresh_token': '' + } + ]; + + const jsonAndTokenHeaders = new Headers({ + 'Content-Type': 'application/json charset=utf-8', + "Authorization": 'Bearer ' + }); + + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [HttpModule], + + providers: [UserService, StoreHeaders, StoreLinks, + { provide: XHRBackend, useClass: MockBackend } + ] + }).compileComponents(); + })); + + it('can instantiate service when inject service', + inject([UserService], (service: UserService) => { + expect(service instanceof UserService).toBe(true); + })); + + + + + it('can provide the mockBackend as XHRBackend', + inject([XHRBackend], (backend: MockBackend) => { + expect(backend).not.toBeNull('backend should be provided'); + })); + + describe('when sendUserDataForRegistration', () => { + let backend: MockBackend; + let service: UserService; + let headers: StoreHeaders; + let links: StoreLinks; + let response: Response; + + beforeEach(inject([Http, XHRBackend, StoreHeaders, StoreLinks], + (http: Http, be: MockBackend, storeHeaders: StoreHeaders, storeLinks: StoreLinks) => { + backend = be; + headers = storeHeaders; + links = storeLinks; + service = new UserService(http, headers, links); + + let options = new ResponseOptions({ status: 200, url: 'http://localhost:63354/User/register', body: mockResponse }); + response = new Response(options); + })); + it('should have expected ', async(inject([], () => { + backend.connections.subscribe((c: MockConnection) => c.mockRespond(response)); + + service.sendUserDataForRegistration("", "", "") + .then(answer => { + expect(answer._body).toBe(mockResponse); + }) + + }))); + it('should be OK returning', async(inject([], () => { + let resp = new Response(new ResponseOptions({ status: 200, body: mockResponse })); + backend.connections.subscribe((c: MockConnection) => { + + c.mockRespond(resp) + expect(c.request.url).toEqual('http://localhost:63354/User/register'); + }); + + service.sendUserDataForRegistration("", "", "") + .then(answer => { + expect(answer._body).toBe(mockResponse); + }) + }))); + it('should treat 404 as error', async(inject([], async() => { + let resp = new Response(new ResponseOptions({ status: 404 })); + backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp)); + + service.sendUserDataForRegistration("", "", "") + .then(answer => { + // fail('should not respond'); + }) + .catch(err => { + expect(err).toMatch(/Bad response status/, 'should catch bad response status code'); + return Promise.reject(null);; // failure is the expected test result + }) + + + }))); + + }); + describe('when checkUser', () => { + let backend: MockBackend; + let service: UserService; + let headers: StoreHeaders; + let links: StoreLinks; + let response: Response; + + beforeEach(inject([Http, XHRBackend, StoreHeaders, StoreLinks], + (http: Http, be: MockBackend, storeHeaders: StoreHeaders, storeLinks: StoreLinks) => { + backend = be; + headers = storeHeaders; + links = storeLinks; + service = new UserService(http, headers, links); + + let options = new ResponseOptions({ status: 200, url: 'http://localhost:63354/User/СheckUser' }); + response = new Response(options); + })); + it('should have expected ', async(inject([], () => { + backend.connections.subscribe((c: MockConnection) => c.mockRespond(response)); + + service.checkUser() + .then(answer => { + expect(answer._body).toBe(undefined); + }) + + }))); + + it('should treat 404 as error', async(inject([], async() => { + let resp = new Response(new ResponseOptions({ status: 404 })); + backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp)); + + service.checkUser() + .then(answer => { + // fail('should not respond'); + }) + .catch(err => { + expect(err).toMatch(/Bad response status/, 'should catch bad response status code'); + return Promise.reject(null);; // failure is the expected test result + }) + + + }))); + + }); + describe('when LogOut', () => { + let backend: MockBackend; + let service: UserService; + let headers: StoreHeaders; + let links: StoreLinks; + let response: Response; + + beforeEach(inject([Http, XHRBackend, StoreHeaders, StoreLinks], + (http: Http, be: MockBackend, storeHeaders: StoreHeaders, storeLinks: StoreLinks) => { + backend = be; + headers = storeHeaders; + links = storeLinks; + service = new UserService(http, headers, links); + + let options = new ResponseOptions({ status: 200, url: 'http://localhost:63354/User/LogOut'}); + response = new Response(options); + })); + it('should have expected ', async(inject([], () => { + backend.connections.subscribe((c: MockConnection) => c.mockRespond(response)); + + service.logOut() + .then(answer => { + expect(answer._body).toBe(null) + }) + + }))); + + it('should treat 404 as error', async(inject([], async() => { + let resp = new Response(new ResponseOptions({ status: 404 })); + backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp)); + + service.logOut() + .then(answer => { + // fail('should not respond'); + }) + .catch(err => { + expect(err).toMatch(/Bad response status/, 'should catch bad response status code'); + return Promise.reject(null);; // failure is the expected test result + }) + + + }))); + + }); + describe('when logIn', () => { + let backend: MockBackend; + let service: UserService; + let headers: StoreHeaders; + let links: StoreLinks; + let response: Response; + + beforeEach(inject([Http, XHRBackend, StoreHeaders, StoreLinks], + (http: Http, be: MockBackend, storeHeaders: StoreHeaders, storeLinks: StoreLinks) => { + backend = be; + headers = storeHeaders; + links = storeLinks; + service = new UserService(http, headers, links); + + let options = new ResponseOptions({ status: 200, url: 'http://localhost:63354/User/register', body: mockResponse }); + response = new Response(options); + })); + it('should have expected ', async(inject([], () => { + backend.connections.subscribe((c: MockConnection) => c.mockRespond(response)); + + service.logIn("", "", "") + .then(answer => { + expect(answer._body).toBe(mockResponse); + }) + + }))); + it('should be OK returning', async(inject([], () => { + let resp = new Response(new ResponseOptions({ status: 200, body: mockResponse })); + backend.connections.subscribe((c: MockConnection) => { + + c.mockRespond(resp) + expect(c.request.url).toEqual('http://localhost:63354/User/LogIn'); + }); + + service.logIn("", "", "") + .then(answer => { + expect(answer._body).toBe(mockResponse); + }) + }))); + it('should treat 404 as error', async(inject([], async() => { + let resp = new Response(new ResponseOptions({ status: 404 })); + backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp)); + + service.logIn("", "", "") + .then(answer => { + // fail('should not respond'); + }) + .catch(err => { + expect(err).toMatch(/Bad response status/, 'should catch bad response status code'); + return Promise.reject(null);; // failure is the expected test result + }) + + + }))); + + }); +}); + + + + diff --git a/src/N2N.Web/src/app/userService.ts b/src/N2N.Web/src/app/userService.ts index 51e3a37..d6c31d6 100644 --- a/src/N2N.Web/src/app/userService.ts +++ b/src/N2N.Web/src/app/userService.ts @@ -27,7 +27,7 @@ export class UserService { { headers: this._storeHeaders.jsonHeader } ) .toPromise() - .then(resp => { debugger; return resp }) + .then(resp => { return resp }) .catch(this.handleError); } @@ -38,21 +38,38 @@ export class UserService { { headers: this._storeHeaders.jsonAndTokenHeaders } ) .toPromise() - .then(resp => { debugger; return resp.json }) + .then(resp => { return resp.json }) .catch(this.handleError); } - logOut(){ + logIn(nickName: string, password: string, capcha: string): Promise{ + var data = { + nickName, + password, + capcha + }; + return this.http.post( + this._storeLinks.logInUrl, + data, + { headers: this._storeHeaders.jsonHeader } + ) + .toPromise() + .then(resp => { debugger; return resp }) + .catch(this.handleError); + } + + logOut(): Promise{ return this.http.delete( this._storeLinks.logoutUrl, { headers: this._storeHeaders.jsonAndTokenHeaders} ) .toPromise() + .then(resp=> {debugger;return resp; localStorage.setItem('Token',"")}) } private handleError(error: any): Promise { - return Promise.reject(error);; + return Promise.reject(error); } }