Skip to content

Commit

Permalink
Merge pull request #72 from navikt/personlookup
Browse files Browse the repository at this point in the history
person lookup, not hooked up to any routes yet
  • Loading branch information
jksolbakken authored Aug 8, 2019
2 parents 0b4326f + 89f5a63 commit 052cfae
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 91 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
}
},
{
"files": ["**/*.test.js"],
"files": ["**/*.test.js", "__mocks__/**/*.js"],
"env": {
"mocha": true,
"node": true,
Expand Down
29 changes: 29 additions & 0 deletions __mocks__/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

jest.genMockFromModule('request');

let stsStatusCode = 0;
let stsResponseBody = {};
let personStatusCode = 0;
let personResponseBody = {};

const setStsStatusCode = code => (stsStatusCode = code);
const setStsResponseBody = body => (stsResponseBody = body);
const setPersonStatusCode = code => (personStatusCode = code);
const setPersonResponseBody = body => (personResponseBody = body);

const get = (url, options, callback) => {
if (url.includes('/sts/')) {
callback(null, { statusCode: stsStatusCode }, stsResponseBody);
} else {
callback(null, { statusCode: personStatusCode }, personResponseBody);
}
};

module.exports = {
get: get,
setStsStatusCode: setStsStatusCode,
setStsResponseBody: setStsResponseBody,
setPersonStatusCode: setPersonStatusCode,
setPersonResponseBody: setPersonResponseBody
};
132 changes: 66 additions & 66 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 15 additions & 4 deletions src/server/authsupport.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
'use strict';

const stillValid = token => {
const isValidNow = token => {
return isValidAt(token, Math.floor(Date.now()) / 1000);
};

const isValidAt = (token, timeInSeconds) => {
if (!token) {
return false;
}

try {
const claims = claimsFrom(token);
const expirationTime = parseInt(claims['exp']);
const nowInSeconds = Math.floor(Date.now() / 1000);
return expirationTime >= nowInSeconds;
return expirationTime >= timeInSeconds;
} catch (err) {
console.log(`error while checking token validity: ${err}`);
return false;
}
};

const willExpireInLessThan = (seconds, token) => {
const timeToTest = Math.floor(Date.now() / 1000) + seconds;
const expirationTime = parseInt(claimsFrom(token)['exp']);
return timeToTest > expirationTime;
};

const validateOidcCallback = (req, azureClient, config) => {
const params = azureClient.callbackParams(req);
const nonce = req.session.nonce;
Expand Down Expand Up @@ -66,7 +75,9 @@ const nameFrom = token => {
};

module.exports = {
stillValid: stillValid,
isValidAt: isValidAt,
isValidNow: isValidNow,
willExpireInLessThan: willExpireInLessThan,
validateOidcCallback: validateOidcCallback,
isMemberOf: isMemberOf,
nameFrom: nameFrom
Expand Down
52 changes: 34 additions & 18 deletions src/server/authsupport.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ const clgOrig = console.log;

afterEach(cleanup);

afterEach(cleanup);

beforeAll(() => {
beforeEach(() => {
// reduce noise in log
console.log = jest.fn();
});
Expand All @@ -17,51 +15,69 @@ afterAll(() => {
console.log = clgOrig;
});

test('valid token has expiry in the future', async () => {
test('valid token has expiry in the future', () => {
const nowInSeconds = Math.floor(Date.now() / 1000);
const token = createToken({ exp: `${nowInSeconds + 10}` });
expect(authsupport.stillValid(token)).toEqual(true);
expect(authsupport.isValidNow(token)).toEqual(true);
});

test('invalid token has expiry in the past', async () => {
test('invalid token has expiry in the past', () => {
const nowInSeconds = Math.floor(Date.now() / 1000);
const token = createToken({ exp: `${nowInSeconds - 10}` });
expect(authsupport.stillValid(token)).toEqual(false);
expect(authsupport.isValidNow(token)).toEqual(false);
});

test('null token does not validate', () => {
expect(authsupport.isValidNow(null)).toEqual(false);
});

test('null token does not validate', async () => {
expect(authsupport.stillValid(null)).toEqual(false);
test('undefined token does not validate', () => {
expect(authsupport.isValidNow(undefined)).toEqual(false);
});

test('undefined token does not validate', async () => {
expect(authsupport.stillValid(undefined)).toEqual(false);
test('malformed token does not validate', () => {
expect(authsupport.isValidNow('bogustext')).toEqual(false);
});

test('malformed token does not validate', async () => {
expect(authsupport.stillValid('bogustext')).toEqual(false);
test('willExpireInLessThan is false if test time is before exp', () => {
const nowInSeconds = Math.floor(Date.now() / 1000);
const token = createToken({ exp: `${nowInSeconds + 30}` });
expect(authsupport.willExpireInLessThan(29, token)).toEqual(false);
});

test('willExpireInLessThan is false if test time is at exp', () => {
const nowInSeconds = Math.floor(Date.now() / 1000);
const token = createToken({ exp: `${nowInSeconds + 30}` });
expect(authsupport.willExpireInLessThan(30, token)).toEqual(false);
});

test('willExpireInLessThan is true if test time is after exp', () => {
const nowInSeconds = Math.floor(Date.now() / 1000);
const token = createToken({ exp: `${nowInSeconds + 30}` });
expect(authsupport.willExpireInLessThan(31, token)).toEqual(true);
});

test('user is member of required group', async () => {
test('user is member of required group', () => {
const token = createToken({ groups: ['group1', 'group2', 'group3'] });
expect(authsupport.isMemberOf('group3', token)).toEqual(true);
});

test('user is not member of required group', async () => {
test('user is not member of required group', () => {
const token = createToken({ groups: ['group1', 'group2', 'group3'] });
expect(authsupport.isMemberOf('group4', token)).toEqual(false);
});

test('name extraction from jwt', async () => {
test('name extraction from jwt', () => {
const token = createToken({ name: 'John Doe' });
expect(authsupport.nameFrom(token)).toEqual('John Doe');
});

test('name extraction from jwt, name property missing', async () => {
test('name extraction from jwt, name property missing', () => {
const token = createToken({});
expect(authsupport.nameFrom(token)).toEqual('unknown user');
});

test('name extraction from jwt, malformed token', async () => {
test('name extraction from jwt, malformed token', () => {
const token = 'invalid bogus';
expect(authsupport.nameFrom(token)).toEqual('unknown user');
});
Expand Down
7 changes: 7 additions & 0 deletions src/server/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@ exports.server = {
port: 3000,
sessionSecret: process.env.SESSION_SECRET
};

exports.nav = {
serviceUserName: process.env.SERVICE_USER_NAME,
serviceUserPassword: process.env.SERVICE_USER_PASSWORD,
stsUrl:
process.env.STS_URL || 'http://security-token-service.svc.nais.local'
};
83 changes: 83 additions & 0 deletions src/server/personlookup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use strict';

const request = require('request');
const authSupport = require('./authsupport');

let navConfig = null;
let cachedAccessToken = null;

const init = config => {
navConfig = config;
cachedAccessToken = null;
};

const hentPerson = async aktørId => {
return new Promise((resolve, reject) => {
logonToNav()
.then(jwt => {
request.get(
`http://sparkel.svc.nais.local/api/person/${aktørId}`,
{
headers: {
Authorization: `Bearer: ${jwt}`
}
},
(error, response, body) => {
if (error || response.statusCode !== 200) {
reject(
`Error during person lookup, got ${
response.statusCode
} ${error || 'unknown error'}`
);
} else {
resolve(body);
}
}
);
})
.catch(err => {
reject(err);
});
});
};

const logonToNav = async () => {
return new Promise((resolve, reject) => {
if (
cachedAccessToken &&
!authSupport.willExpireInLessThan(30, cachedAccessToken)
) {
resolve(cachedAccessToken);
} else {
request.get(
`${navConfig.stsUrl}/rest/v1/sts/token?grant_type=client_credentials&scope=openid`,
{
headers: {
Authorization:
'Basic ' +
Buffer.from(
`${navConfig.serviceUserName}:${navConfig.serviceUserPassword}`
).toString('base64')
}
},
(error, response, body) => {
if (error || response.statusCode !== 200) {
reject(
`Error during STS login, got ${
response.statusCode
} ${error || 'unknown error'}`
);
} else {
cachedAccessToken = body.access_token;
resolve(body.access_token);
}
}
);
}
});
};

module.exports = {
init: init,
hentPerson: hentPerson
};
46 changes: 46 additions & 0 deletions src/server/personlookup.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

const request = require('request');

const personLookup = require('../../src/server/personlookup');

beforeEach(() => {
personLookup.init({
serviceUserName: 'da_usah',
serviceUserPassword: 'pazzwd',
stsUrl: 'http://localhost'
});
});

test('successful person lookup resolves with person object', () => {
request.setStsStatusCode(200);
request.setStsResponseBody({
access_token: 'some_token'
});
request.setPersonStatusCode(200);
request.setPersonResponseBody({ navn: 'Navn Navnesen' });

expect(personLookup.hentPerson('12345')).resolves.toEqual({
navn: 'Navn Navnesen'
});
});

test('logon failure -> rejection', () => {
request.setStsStatusCode(500);

expect(personLookup.hentPerson('12345')).rejects.toMatch(
'Error during STS login'
);
});

test('lookup failure -> rejection', () => {
request.setStsStatusCode(200);
request.setStsResponseBody({
access_token: 'some_token'
});
request.setPersonStatusCode(500);

expect(personLookup.hentPerson('12345')).rejects.toMatch(
'Error during person lookup'
);
});
2 changes: 1 addition & 1 deletion src/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ app.post('/callback', (req, res) => {
app.use('/*', (req, res, next) => {
if (
process.env.NODE_ENV === 'development' ||
authsupport.stillValid(req.session.spadeToken)
authsupport.isValidNow(req.session.spadeToken)
) {
next();
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/server/storage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ beforeAll(done => {
setupServer();
storage
.init(`localhost:${port}`, 'anAccessKeyId', 'aSecretAccessKey')
.then(result => {
.then(() => {
done();
})
.catch(err => {
Expand Down

0 comments on commit 052cfae

Please sign in to comment.