Skip to content

Commit

Permalink
Merged PR 1453: Merge staging to master
Browse files Browse the repository at this point in the history
Promote version 9.6 to master.

Related work items: #41885, #41895, #43422, #44203, #44460, #44464, #44468, #44472, #44476, #44500, #44504, #44506, #44510, #44514, #44516, #44518, #44522, #44526, #44530
  • Loading branch information
bmcorum committed Jun 12, 2020
2 parents 02135a3 + df86eab commit 1a92ee3
Show file tree
Hide file tree
Showing 39 changed files with 1,653 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Badge | Status
--- | ---
**Circle CI Build** | [![Build Status](https://circleci.com/gh/openopps/openopps-platform.svg?style=shield&circle-token=fc05032b464335e120cd4133f124a3b694bc5d2b)](https://circleci.com/gh/openopps/openopps-platform)
**Code Climate** | [![Code Climate](https://codeclimate.com/github/openopps/openopps-platform/badges/gpa.svg)](https://codeclimate.com/github/openopps/openopps-platform)
**Current Version** | [![Current Version](https://img.shields.io/badge/release-v9.5.0-0e5487.svg)](https://github.com/openopps/openopps-platform/releases)
**Current Version** | [![Current Version](https://img.shields.io/badge/release-v9.6.0-0e5487.svg)](https://github.com/openopps/openopps-platform/releases)

---

Expand Down
2 changes: 1 addition & 1 deletion api/admin/sql/getTasksByState.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
with tasks as (
select task.*, community.target_audience, community.is_disabled as community_is_disabled,
select task.*, community.target_audience, community.is_disabled as community_is_disabled, community.community_name,
(select row_to_json (muser)
from (
select
Expand Down
2 changes: 2 additions & 0 deletions api/auth/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,14 @@ passport.serializeUser(function (user, done) {
done(null, {
id: user.id,
tokenset: user.tokenset,
urls: user.urls,
});
});

passport.deserializeUser(async function (userObj, done) {
var user = await fetchUser(userObj.id);
user.tokenset = userObj.tokenset;
user.urls = userObj.urls;
done(null, user);
});

Expand Down
137 changes: 106 additions & 31 deletions api/auth/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const log = require('log')('app:syncProfile');
const db = require('../../db');
const dao = require('./dao')(db);
const _ = require('lodash');
const util = require('util');
const request = require('request');
const elasticService = require('../../elastic/service');
const Agency = require('../model/Agency');
Expand Down Expand Up @@ -62,6 +63,11 @@ async function updateProfileData (user, profile, tokenset) {
elasticService.deleteUser(user.id);
} catch (err) {}
}
user.urls = {
profileHome: profile.ProfileHome,
profileDocuments: profile.ProfileDocuments,
dashboardHome: profile.DashboardHome,
};
return user;
}

Expand Down Expand Up @@ -101,37 +107,106 @@ function syncAutoJoinCommunities (user) {
});
}

module.exports = {
get: async (tokenset) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, { url: openopps.auth.profileURL, auth: { 'bearer': tokenset.access_token } }), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: 'Error getting user profile.' });
} else {
resolve(JSON.parse(res.body));
}
});
module.exports = {};

module.exports.get = async (tokenset) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, {
url: openopps.auth.profileURL,
method: 'GET',
auth: { 'bearer': tokenset.access_token },
json: true,
}), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: 'Error getting user profile.' });
} else {
resolve(res.body);
}
});
},
getDocuments: async (tokenset, documentType) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, {
url: openopps.auth.profileDocumentURL + (documentType ? '/' + documentType : ''),
auth: { 'bearer': tokenset.access_token },
}), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: 'Error getting user documents.' });
} else {
resolve(JSON.parse(res.body));
}
});
});
};

module.exports.getDocuments = async (tokenset, documentType) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, {
url: openopps.auth.profileDocumentURL + (documentType ? '/' + documentType : ''),
method: 'GET',
auth: { 'bearer': tokenset.access_token },
json: true,
}), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: 'Error getting user documents.' });
} else {
resolve(res.body);
}
});
},
sync: async (user, tokenset, callback) => {
await module.exports.get(tokenset).then(async (profile) => {
user = await updateProfileData(user, profile, tokenset);
await syncAutoJoinCommunities(user);
callback(null, user);
}).catch(callback);
},
});
};

module.exports.sync = async (user, tokenset, callback) => {
await module.exports.get(tokenset).then(async (profile) => {
user = await updateProfileData(user, profile, tokenset);
await syncAutoJoinCommunities(user);
callback(null, user);
}).catch(callback);
};

module.exports.grantDocumentAccess = async (tokenset, documentId, applicationId) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, {
url: [openopps.auth.profileDocumentURL, documentId, 'grant-access'].join('/'),
method: 'POST',
auth: { 'bearer': tokenset.access_token },
body: applicationId,
json: true,
}), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: util.format('Error getting grant token for document %s.', documentId) });
} else {
resolve(res.body);
}
});
});
};

module.exports.revokeDocumentAccess = async (tokenset, document, applicationId) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, {
url: [openopps.auth.profileDocumentURL, document.documentId, 'revoke-grant-access'].join('/'),
method: 'POST',
auth: { 'bearer': tokenset.access_token },
json: true,
body: {
GrantAccess: document.grantAccess,
ApplicationId: applicationId,
},
}), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: util.format('Error revoking grant token for document %s.', document.documentId) });
} else {
resolve();
}
});
});
};

module.exports.getDocumentAccess = async (tokenset, document, applicationId) => {
return new Promise((resolve, reject) => {
request(_.extend(requestOptions, {
url: [openopps.auth.documentAccessURL, document.documentId, 'document-access'].join('/'),
method: 'POST',
auth: { 'bearer': tokenset.access_token },
json: true,
body: {
GrantAccess: document.grantAccess,
ApplicationId: applicationId,
},
}), async (err, res) => {
if(err || res.statusCode !== 200) {
reject({ message: util.format('Error getting document token for document %s.', document.documentId) });
} else {
resolve(res.body);
}
});
});
};
49 changes: 48 additions & 1 deletion api/volunteer/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ router.post('/api/volunteer', auth, async (ctx, next) => {
if (await service.canAddVolunteer(ctx.request.body, ctx.state.user)) {
var attributes = ctx.request.body;
attributes.userId = ctx.state.user.id;
await service.addVolunteer(attributes, function (err, volunteer) {
await service.addVolunteer(ctx.state.user.tokenset, attributes, function (err, volunteer) {
if (err) {
return ctx.body = err;
}
Expand All @@ -29,6 +29,53 @@ router.post('/api/volunteer', auth, async (ctx, next) => {
}
});

router.get('/api/volunteer/user/resumes', auth, auth.checkToken, async (ctx, next) => {
await service.getResumes(ctx.state.user).then(resumes => {
ctx.status = 200;
ctx.body = {
key: ctx.state.user.tokenset.access_token,
resumes: resumes,
};
}).catch(err => {
ctx.status = 404;
});
});

router.get('/api/volunteer/:id/resume', auth, auth.checkToken, async (ctx, next) => {
await service.getVolunteerResumeAccess(ctx.state.user.tokenset, ctx.params.id, ctx.query.taskId).then(result => {
ctx.status = 200;
ctx.body = {
key: ctx.state.user.tokenset.access_token,
url: result,
};
}).catch(err => {
ctx.status = 400;
});
});

router.get('/api/volunteer/:id', auth, async (ctx, next) => {
await service.getVolunteer(ctx.params.id,ctx.query.taskId).then(result => {
ctx.body = result[0];
}).catch(err => {
ctx.status = 400;
});
});

router.put('/api/volunteer/:id', auth, async (ctx, next) => {
var attributes = ctx.request.body;
attributes.id= ctx.params.id;
attributes.updatedAt = new Date();
await service.updateVolunteer(ctx.state.user.tokenset, attributes, async (errors, result) => {
if (errors) {
ctx.status = 400;
ctx.body = errors;
} else {
ctx.status = 200;
ctx.body = result;
}
});
});

router.post('/api/volunteer/delete', auth, async (ctx, next) => {
var attributes = ctx.request.body;
attributes.userId = ctx.state.user.id;
Expand Down
92 changes: 90 additions & 2 deletions api/volunteer/service.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
const _ = require ('lodash');
const util = require('util');
const crypto = require('crypto-wrapper');
const log = require('log')('app:volunteer:service');
const db = require('../../db');
const dao = require('./dao')(db);
const notification = require('../notification/service');
const Profile = require('../auth/profile');

async function addVolunteer (attributes, done) {
async function addVolunteer (tokenset, attributes, done) {
var volunteer = await dao.Volunteer.find('"taskId" = ? and "userId" = ?', attributes.taskId, attributes.userId);
if (volunteer.length == 0) {
attributes.createdAt = new Date();
attributes.updatedAt = new Date();
if (attributes.resumeId) {
var grantKey = await Profile.grantDocumentAccess(tokenset, attributes.resumeId, attributes.taskId);
var encryptedKey = crypto.encrypt(grantKey.Key);
attributes.grantAccess = encryptedKey.data;
attributes.iv = encryptedKey.iv;
attributes.nonce = grantKey.Nonce;
}
await dao.Volunteer.insert(attributes).then(async (volunteer) => {
return done(null, volunteer);
}).catch(err => {
Expand Down Expand Up @@ -122,6 +132,80 @@ async function sendAddedVolunteerNotification (user, volunteer, action) {
}
}

async function getResumes (user) {
return new Promise((resolve, reject) => {
Profile.getDocuments(user.tokenset, 'secure_resume').then(documents => {
resolve(documents);
}).catch((err) => {
reject(err);
});
});
}

async function getVolunteerResumeAccess (tokenset, id) {
return new Promise((resolve, reject) => {
dao.Volunteer.findOne('id = ?', id).then(volunteer => {
var document = {
documentId: volunteer.resumeId,
grantAccess: {
Key: crypto.decrypt(volunteer.grantAccess, volunteer.iv),
Nonce: volunteer.nonce,
},
};
Profile.getDocumentAccess(tokenset, document, volunteer.taskId).then(documentAccess => {
var baseURL = [openopps.auth.documentAccessURL, document.documentId].join('/');
var params = util.format('k=%s&n=%s&e=%s', documentAccess.Key, documentAccess.Nonce, documentAccess.Expire);
resolve([baseURL, params].join('?'));
}).catch((err) => {
reject(err);
});
}).catch(err => {
reject(err);
});
});
}

async function getVolunteer (volunteerId,taskId) {
return (await dao.Volunteer.find('id = ? and "taskId" = ?',volunteerId,taskId).catch(() => { return []; }));
}

async function updateVolunteer (tokenset, attributes, done) {
return await dao.Volunteer.findOne('id = ?', attributes.id).then(async (vol) => {
if (attributes.resumeId != vol.resumeId) {
if (vol.grantAccess && vol.iv) {
var document = {
documentId: vol.resumeId,
grantAccess: {
Key: crypto.decrypt(vol.grantAccess, vol.iv),
Nonce: vol.nonce,
},
};
await Profile.revokeDocumentAccess(tokenset, document, vol.taskId).catch((err) => {
log.error(err);
return done({ message: 'An unexpected error occured trying to update your application.'});
});
}
if (attributes.resumeId) {
var grantKey = await Profile.grantDocumentAccess(tokenset, attributes.resumeId, attributes.taskId);
var encryptedKey = crypto.encrypt(grantKey.Key);
attributes.grantAccess = encryptedKey.data;
attributes.iv = encryptedKey.iv;
attributes.nonce = grantKey.Nonce;
}
}
return await dao.Volunteer.update(attributes).then(async (volunteer) => {
return done(null, volunteer);
}).catch((err) => {
log.error(err);
return done({ message: 'An unexpected error occured trying to update your application.'});
});
}).catch((err) => {
log.error(err);
return done({ message: 'An unexpected error occured trying to update your application.'});
});
}


async function sendDeletedVolunteerNotification (notificationInfo, action) {
var data = {
action: action,
Expand All @@ -134,7 +218,7 @@ async function sendDeletedVolunteerNotification (notificationInfo, action) {
data.model.community = await dao.Community.findOne('community_id = (select community_id from task where id = ?)', notificationInfo.id).catch(() => { return null; });
if(!notificationInfo.bounced) {
notification.createNotification(data);
}
}
}

module.exports = {
Expand All @@ -147,4 +231,8 @@ module.exports = {
sendAddedVolunteerNotification: sendAddedVolunteerNotification,
sendDeletedVolunteerNotification: sendDeletedVolunteerNotification,
selectVolunteer: selectVolunteer,
getResumes: getResumes,
getVolunteer: getVolunteer,
updateVolunteer: updateVolunteer,
getVolunteerResumeAccess: getVolunteerResumeAccess,
};
1 change: 1 addition & 0 deletions app/security-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const policy = {
'connect-src': [
'self',
'*.usajobs.gov',
'*.usajobs.local',
],
'img-src': [
'self',
Expand Down
Loading

0 comments on commit 1a92ee3

Please sign in to comment.