-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* create adapter for passport strategies * create unit test for adapter
- Loading branch information
1 parent
8ae39b0
commit 920c58d
Showing
11 changed files
with
215 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,3 @@ | |
|
||
// NOTE(bajtos) This file is used by VSCode/TypeScriptServer at dev time only | ||
export * from './src'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Copyright IBM Corp. 2013,2017. All Rights Reserved. | ||
// Node module: loopback | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
import * as http from 'http'; | ||
import {ServerRequest as Request} from 'http'; | ||
import {HttpErrors, ParsedRequest} from '@loopback/core'; | ||
|
||
/** | ||
* Interface definition of a passport strategy. | ||
*/ | ||
export interface Strategy { | ||
authenticate(req: http.ServerRequest): void; | ||
} | ||
|
||
/** | ||
* Shimmed Request to satisfy express requirements of passport strategies. | ||
*/ | ||
export class ShimRequest { | ||
headers: Object; | ||
query: Object; | ||
url: string; | ||
path: string; | ||
method: string; | ||
constructor(request?: ParsedRequest) { | ||
if (request) { | ||
this.headers = request.headers; | ||
this.query = request.query; | ||
this.url = request.url; | ||
this.path = request.path; | ||
this.method = request.method; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Adapter class to invoke a passport strategy. | ||
* Instance is created by passing a passport strategy in the constructor | ||
*/ | ||
export class StrategyAdapter { | ||
private strategyCtor: Strategy; | ||
|
||
constructor(strategy: Strategy) { | ||
this.strategyCtor = strategy; | ||
} | ||
|
||
/** | ||
* The function to invoke the contained passport strategy. | ||
* 1. Create an instance of the strategy | ||
* 2. add success and failure state handlers | ||
* 3. authenticate using the strategy | ||
* @param req {http.ServerRequest} The incoming request. | ||
*/ | ||
authenticate(req: ParsedRequest) { | ||
const shimReq = new ShimRequest(req); | ||
return new Promise<Object>((resolve, reject) => { | ||
// create an instance of the strategy | ||
const strategy = Object.create(this.strategyCtor); | ||
const self = this; | ||
|
||
// add success state handler to strategy instance | ||
strategy.success = function(user: object) { | ||
resolve(user); | ||
}; | ||
|
||
// add failure state handler to strategy instance | ||
strategy.fail = function(challenge: string) { | ||
reject(new HttpErrors.Unauthorized(challenge)); | ||
}; | ||
|
||
// add error state handler to strategy instance | ||
strategy.error = function(error: string) { | ||
reject(new HttpErrors.InternalServerError(error)); | ||
}; | ||
|
||
// authenticate | ||
strategy.authenticate(shimReq); | ||
}); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
packages/authentication/test/unit/fixtures/mock-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright IBM Corp. 2013,2017. All Rights Reserved. | ||
// Node module: loopback | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
/** | ||
* Test fixture for an asynchronous strategy | ||
*/ | ||
import {ParsedRequest} from '@loopback/core'; | ||
|
||
export class MockStrategy { | ||
private mockUser: Object; | ||
constructor() {} | ||
setMockUser(userObj: Object) { | ||
this.mockUser = userObj; | ||
} | ||
async verify(req: ParsedRequest) { | ||
if (req.query && req.query.testState && req.query.testState === 'fail') { | ||
this.returnUnAuthourized({error: 'authorization failed'}); | ||
return; | ||
} else if (req.query && req.query.testState && req.query.testState === 'error') { | ||
this.returnError('unexpected error'); | ||
return; | ||
} | ||
process.nextTick(this.returnMockUser.bind(this)); | ||
} | ||
returnMockUser() { | ||
this.success(this.mockUser); | ||
} | ||
returnUnAuthourized(challenge: Object) { | ||
this.fail(challenge); | ||
} | ||
returnError(err: string) { | ||
this.error(err); | ||
} | ||
async authenticate(req: ParsedRequest) { | ||
await this.verify(req); | ||
} | ||
success(user: Object) { | ||
throw new Error('should be overrided by adapter'); | ||
} | ||
fail(challenge: Object) { | ||
throw new Error('should be overrided by adapter'); | ||
} | ||
error(error: string) { | ||
throw new Error('should be overrided by adapter'); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
packages/authentication/test/unit/strategy-adapter.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright IBM Corp. 2013,2017. All Rights Reserved. | ||
// Node module: loopback | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {expect} from '@loopback/testlab'; | ||
import {ShimRequest} from '../..'; | ||
import {StrategyAdapter} from '../..'; | ||
import {ParsedRequest, HttpErrors} from '@loopback/core'; | ||
import {MockStrategy} from './fixtures/mock-strategy'; | ||
|
||
const passportStrategy = require('passport-strategy'); | ||
|
||
describe('Strategy Adapter', () => { | ||
const mockUser: User = {id: 'mock-user', role: 'mock-role'}; | ||
interface User { | ||
id: string; | ||
role: string; | ||
} | ||
|
||
describe('authenticate()', () => { | ||
it('calls the authenticate method of the strategy', () => { | ||
const strategy = new passportStrategy(); | ||
let calledFlag = false; | ||
// TODO: (as suggested by @bajtos) use sinon spy | ||
strategy.authenticate = function() { | ||
calledFlag = true; | ||
}; | ||
const adapter = new StrategyAdapter(strategy); | ||
const request = <ParsedRequest> {}; | ||
const user: Object = adapter.authenticate(request); | ||
expect(calledFlag).to.be.true(); | ||
}); | ||
|
||
it('returns a promise which resolves to an object', async () => { | ||
const strategy = new MockStrategy(); | ||
strategy.setMockUser(mockUser); | ||
const adapter = new StrategyAdapter(strategy); | ||
const request = <ParsedRequest> {}; | ||
const user: Object = await adapter.authenticate(request); | ||
expect(user).to.be.eql(mockUser); | ||
}); | ||
|
||
it('throws Unauthorized error when authentication fails', async () => { | ||
const strategy = new MockStrategy(); | ||
strategy.setMockUser(mockUser); | ||
const adapter = new StrategyAdapter(strategy); | ||
const request = <ParsedRequest> {}; | ||
request.query = {testState: 'fail'}; | ||
try { | ||
const user: Object = await adapter.authenticate(request); | ||
} catch (err) { | ||
expect(err).to.be.instanceof(HttpErrors.Unauthorized); | ||
} | ||
}); | ||
|
||
it('throws InternalServerError when strategy returns error', async () => { | ||
const strategy = new MockStrategy(); | ||
strategy.setMockUser(mockUser); | ||
const adapter = new StrategyAdapter(strategy); | ||
const request = <ParsedRequest> {}; | ||
request.query = {testState: 'error'}; | ||
try { | ||
const user: Object = await adapter.authenticate(request); | ||
} catch (err) { | ||
expect(err).to.be.instanceof(HttpErrors.InternalServerError); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,3 @@ | |
|
||
// NOTE(bajtos) This file is used by VSCode/TypeScriptServer at dev time only | ||
export * from './src'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters