Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: call gsuite-wrapper on key moments #183

Draft
wants to merge 7 commits into
base: stable
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const config = {
url: 'http://mailer',
port: 4000
},
gsuiteWrapper: {
url: 'http://gsuite-wrapper',
port: 8084
},
logger: {
silent: false,
level: process.env.LOGLEVEL || 'info'
Expand Down
3 changes: 2 additions & 1 deletion lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ module.exports = {
NEW_JOIN_REQUEST: 'MyAEGEE: New join request for your body',
NEW_MEMBER: 'MyAEGEE: Welcome to AEGEE'
},
RESTRICTED_EMAILS: ['aegee.org', 'aegee.eu']
RESTRICTED_EMAILS: ['aegee.org', 'aegee.eu'],
GSUITE_DOMAIN: 'aegee.eu'
};
9 changes: 9 additions & 0 deletions lib/cron.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const cron = require('node-cron');
const request = require('request-promise-native');
const _ = require('lodash');

const config = require('../config');
const logger = require('./logger');
const {
MailConfirmation,
Expand All @@ -25,6 +27,13 @@ const JobCallbacks = {
}, 'Deleting not confirmed user');

await confirmation.destroy();
// TODO: check that GSuite account is also deleted
if (user.gsuite_id) {
await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts/' + user.gsuite_id,
method: 'DELETE'
});
}
await user.destroy();

logger.info('Deleted.');
Expand Down
6 changes: 6 additions & 0 deletions middlewares/bodies.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ exports.createBody = async (req, res) => {
return errors.makeForbiddenError(res, 'Permission global:create:body is required, but not present.');
}

// TODO: if antenna, contact antenna or contact, then create GSuite account
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be done later


// TODO: filter out fields that are changed in the other way
const body = await Body.create(req.body);
return res.json({
Expand All @@ -65,6 +67,8 @@ exports.updateBody = async (req, res) => {
return errors.makeForbiddenError(res, 'Permission update:body is required, but not present.');
}

// TODO: if antenna, contact antenna or contact, then update GSuite account if needed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be done later


await req.currentBody.update(req.body, { fields: req.permissions.getPermissionFilters('update:body') });
return res.json({
success: true,
Expand All @@ -89,6 +93,7 @@ exports.setBodyStatus = async (req, res) => {
await BodyMembership.destroy({ where: { body_id: req.currentBody.id }, transaction: t });
await Circle.destroy({ where: { body_id: req.currentBody.id }, transaction: t });
await Payment.destroy({ where: { body_id: req.currentBody.id }, transaction: t });
// TODO: suspend GSuite account (if attached)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be done later

});

return res.json({
Expand Down Expand Up @@ -122,6 +127,7 @@ exports.createMember = async (req, res) => {
body_id: req.currentBody.id
});

// TODO: create active GSuite account
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be done later

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, this should create a GSuite account for new users. That should be implemented at the start already

return res.json({
success: true,
data: membership
Expand Down
27 changes: 27 additions & 0 deletions middlewares/campaigns.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
const crypto = require('crypto');
const request = require('request-promise-native');
const config = require('../config');
const {
User,
MailConfirmation,
Expand Down Expand Up @@ -33,14 +36,38 @@ exports.registerUser = async (req, res) => {
transaction: t
});

// TODO: add uniqueness check; transliterate gsuiteEmail in case of umlauts or other special characters
// TODO: probably move this elsewhere since not all MyAEGEE users need a GSuite account
const gsuiteEmail = req.body.first_name + '.' + req.body.last_name + '@' + constants.GSUITE_DOMAIN;
const payload = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to do it this way: have a file to contain all gsuite functions (or even better, make it a class, like GSuiteClient, create instance of it and assign it to something like req.gsuiteClient), then you can reference this class instead of having this logic in a middleware itself

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea

primaryEmail: gsuiteEmail.toLowerCase().replace(' ', ''),
name: {
givenName: req.body.first_name,
familyName: req.body.last_name
},
secondaryEmail: req.body.email,
password: crypto.createHash('sha1').update(JSON.stringify(req.body.password)).digest('hex'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge1peshcoff this is what needs to be changed in GSuiteWrapper

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah okay, haven't seen this comment somehow

Copy link
Member

@WikiRik WikiRik Jul 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because we use the same function in two places. So it's good if we move it to a class where we can just define it once and call it twice

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree 100%

userPK: req.body.username,
antenna: 'Undefined-yet'
};

// Adding a person to a body if campaign has the autojoin body.
if (campaign.autojoin_body_id) {
await BodyMembership.create({
user_id: user.id,
body_id: campaign.autojoin_body_id
}, { transaction: t });

payload.antenna = campaign.autojoin_body_id;
}

await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts',
method: 'POST',
json: true,
body: payload
});

const confirmation = await MailConfirmation.createForUser(user.id, t);

await mailer.sendMail({
Expand Down
6 changes: 6 additions & 0 deletions middlewares/circle-memberships.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ exports.createMembership = async (req, res) => {
user_id: user.id
});

// TODO: check if user got the permission for an active GSuite account, if true activate user

return res.json({
success: true,
data: circleMembership
Expand Down Expand Up @@ -93,6 +95,8 @@ exports.deleteMembership = async (req, res) => {

await req.currentCircleMembership.destroy();

// TODO: check if user still has the permission for an active GSuite account, otherwise suspend GSuite account

return res.json({
success: true,
message: 'Membership is deleted.'
Expand All @@ -110,6 +114,8 @@ exports.deleteOwnMembership = async (req, res) => {

await circleMembership.destroy();

// TODO: check if user still has the permission for an active GSuite account, otherwise suspend GSuite account

return res.json({
success: true,
message: 'Membership is deleted.'
Expand Down
92 changes: 91 additions & 1 deletion middlewares/members.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const moment = require('moment');
const _ = require('lodash');

const request = require('request-promise-native');
const crypto = require('crypto');
const config = require('../config');
const { User, Body, MailChange, MailConfirmation } = require('../models');
const constants = require('../lib/constants');
const helpers = require('../lib/helpers');
Expand Down Expand Up @@ -122,6 +124,24 @@ exports.updateUser = async (req, res) => {
}

await req.currentUser.update(req.body, { fields: constants.FIELDS_TO_UPDATE.USER.UPDATE });

// TODO: update first/late name to GSuite account (if there is an account attached)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mention it here, but it goes for the whole file; not every user will have a GSuite account attached (mostly older accounts) so we also need to check that we only do things if an account is attached

if (req.currentUser.gsuite_id && (req.body.first_name || req.body.last_name)) {
const payload = {
name: {
givenName: req.body.first_name || req.currentUser.first_name,
familyName: req.body.last_name || req.currentUser.last_name
}
};

await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts/' + req.currentUser.gsuite_id,
method: 'PUT',
json: true,
body: payload
});
}

return res.json({
success: true,
data: req.currentUser
Expand All @@ -133,7 +153,16 @@ exports.deleteUser = async (req, res) => {
return errors.makeForbiddenError(res, 'Permission delete:member is required, but not present.');
}

// TODO: if user gets deleted the gsuite account also gets deleted (if there was an account attached)
if (req.currentUser.gsuite_id) {
await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts/' + req.currentUser.gsuite_id,
method: 'DELETE'
});
}

await req.currentUser.destroy();

return res.json({
success: true,
message: 'User is deleted.'
Expand All @@ -153,6 +182,19 @@ exports.setUserPassword = async (req, res) => {

await userWithPassword.update({ password: req.body.password });

// TODO: password should be sent to gsuite-wrapper
if (req.currentUser.gsuite_id) {
const payload = {
password: crypto.createHash('sha1').update(JSON.stringify(req.body.password)).digest('hex')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github complains here, is it okay? if it is, can it be muted?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be fixed in GSuiteWrapper itself first, we can temporarily mute it if we merge this before

};
await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts/' + req.currentUser.gsuite_id,
method: 'PUT',
json: true,
body: payload
});
}

// TODO: add a mail that the password was changed.

return res.json({
Expand All @@ -167,6 +209,7 @@ exports.setUserActive = async (req, res) => {
}

await req.currentUser.update({ active: req.body.active });

return res.json({
success: true,
data: req.currentUser
Expand All @@ -186,6 +229,16 @@ exports.confirmUser = async (req, res) => {

await confirmation.destroy();

// TODO: probably move this to another method since not all MyAEGEE members should have a GSuite account
if (req.currentUser.gsuite_id) {
await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts/' + req.currentUser.gsuite_id,
method: 'PUT',
json: true,
body: { suspended: false }
});
}

return res.json({
success: true,
data: req.currentUser
Expand All @@ -207,9 +260,37 @@ exports.setPrimaryBody = async (req, res) => {
return errors.makeForbiddenError(res, 'User is not a member of this body.');
}

// TODO: set GSuite department to primary body
await req.currentUser.update({ primary_body_id: body.id });
// await request({
// url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts?DEPARTMENT',
// method: 'POST',
// json: true,
// body:
// });

// await request({
// url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/groups?DEPARTMENT',
// method: 'POST',
// json: true,
// body:
// });
} else {
// TODO: set GSuite department to primary body
await req.currentUser.update({ primary_body_id: null });
// await request({
// url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts?DEPARTMENT',
// method: 'POST',
// json: true,
// body:
// });

// await request({
// url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/groups?DEPARTMENT',
// method: 'POST',
// json: true,
// body:
// });
}

return res.json({
Expand Down Expand Up @@ -276,9 +357,18 @@ exports.confirmEmailChange = async (req, res) => {
return errors.makeNotFoundError(res, 'Token is expired.');
}

// TODO: change GSuite secondary email
await sequelize.transaction(async (t) => {
await mailChange.user.update({ email: mailChange.new_email }, { transaction: t });
await mailChange.destroy({ transaction: t });
if (req.currentUser.gsuite_id) {
await request({
url: config.gsuiteWrapper.url + ':' + config.gsuiteWrapper.port + '/accounts/' + req.currentUser.gsuite_id,
method: 'PUT',
json: true,
body: { secondaryEmail: mailChange.new_email }
});
}
});

return res.json({
Expand Down
Loading