Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Split usermapping table. Add organisation member. #417

Merged
merged 2 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Github team access: Add member into organisation before add to team.
  • Loading branch information
afrisalyp committed Jul 11, 2021
commit 894f30f328ea85d46b14d72b3ec71d44b133bd10
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"heroku-postbuild": "gulp build",
"create-tables": "CREATE_DB=true node scripts/create-update-tables.js",
"migrate-user-mapping": "node scripts/migrate-user-mapping.js",
"add-organisation": "node scripts/add-organisation.js",
"log-repository-collisions": "node scripts/log-repository-collisions.js"
},
"dependencies": {
Expand Down
33 changes: 33 additions & 0 deletions scripts/add-organisation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const dbHelper = require('../src/common/db-helper');
const helper = require('../src/common/helper');
const Organisation = require('../src/models').Organisation;

const args = process.argv;
if (args.length < 5) {
console.log('Please provide data. Example: npm run add-organisation MyOrganisation ownername PAT-Token');
return;
}
const organisationName = args[2];
const owner = args[3];
const pat = args[4];

(async () => {
const dbOrganisation = await dbHelper.queryOneOrganisation(Organisation, organisationName);
if (dbOrganisation) {
console.log(`Updating Organisation = ${organisationName} Owner = ${owner} PAT = ${pat}.`);
await dbHelper.update(Organisation, dbOrganisation.id, {
name: organisationName,
owner,
personalAccessToken: pat
});
}
else {
console.log(`Adding Organisation = ${organisationName} Owner = ${owner} PAT = ${pat}.`);
await dbHelper.create(Organisation, {
id: helper.generateIdentifier(),
name: organisationName,
owner,
personalAccessToken: pat
});
}
})();
1 change: 0 additions & 1 deletion scripts/migrate-user-mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const dbHelper = require('../src/common/db-helper');
const GithubUserMapping = require('../src/models').GithubUserMapping;
const GitlabUserMapping = require('../src/models').GitlabUserMapping;

console.log(process.env.IS_LOCAL);
if (process.env.IS_LOCAL) {
AWS.config.update({
endpoint: 'http://localhost:8000'
Expand Down
20 changes: 20 additions & 0 deletions src/common/db-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,25 @@ async function removeUser(Model, username, type) {
});
}

/**
* Get single data by query parameters
* @param {Object} model The dynamoose model to query
* @param {String} organisation The organisation name
* @returns {Promise<void>}
*/
async function queryOneOrganisation(model, organisation) {
return await new Promise((resolve, reject) => {
model.queryOne('name').eq(organisation)
.all()
.exec((err, result) => {
if (err) {
logger.debug(`queryOneOrganisation. Error. ${err}`);
return reject(err);
}
return resolve(result);
});
});
}

module.exports = {
getById,
Expand All @@ -379,6 +398,7 @@ module.exports = {
queryOneActiveCopilotPayment,
queryOneActiveProject,
queryOneActiveProjectWithFilter,
queryOneOrganisation,
queryOneIssue,
queryOneUserByType,
queryOneUserByTypeAndRole,
Expand Down
18 changes: 15 additions & 3 deletions src/controllers/GithubController.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ async function addUserToTeam(req, res) {
config.GITHUB_CLIENT_ID
}&redirect_uri=${
encodeURIComponent(callbackUri)
}&scope=${
encodeURIComponent('admin:org')
}&state=${identifier}`);
}

Expand Down Expand Up @@ -156,15 +158,25 @@ async function addUserToTeamCallback(req, res) {
throw new errors.UnauthorizedError('Github authorization failed.', result.body.error_description);
}
const token = result.body.access_token;

// get team details
const teamDetails = await GithubService.getTeamDetails(team.ownerToken, team.teamId);
const organisation = teamDetails.organization.login;

// Add member to organisation
const addOrganisationResult = await GithubService.addOrganisationMember(organisation, token);
console.log(`Add organisation member, state = ${addOrganisationResult.state}`); /* eslint-disable-line no-console */
if (addOrganisationResult.state === 'pending') {
const acceptInvitation = await GithubService.acceptOrganisationInvitation(organisation, token);
console.log(`Accept organisation invitation by member, state = ${acceptInvitation.state}`); /* eslint-disable-line no-console */
}

// add user to team
console.log(`adding ${token} to ${team.teamId} with ${team.ownerToken}`); /* eslint-disable-line no-console */
const githubUser = await GithubService.addTeamMember(team.teamId, team.ownerToken, token, team.accessLevel);
// associate github username with TC username
const mapping = await dbHelper.queryOneUserMappingByTCUsername(GithubUserMapping, req.session.tcUsername);

// get team details
const teamDetails = await GithubService.getTeamDetails(team.ownerToken, team.teamId);

if (mapping) {
await dbHelper.update(GithubUserMapping, mapping.id, {
githubUsername: githubUser.username,
Expand Down
36 changes: 36 additions & 0 deletions src/models/Organisation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* This defines organisation model.
*/
'use strict';

const dynamoose = require('dynamoose');

const Schema = dynamoose.Schema;

const schema = new Schema({
id: {
type: String,
required: true,
hashKey: true
},
name: {
type: String,
required: true,
index: {
global: true,
project: true,
rangKey: 'id',
name: 'NameIndex'
}
},
owner: {
type: String,
required: true
},
personalAccessToken: {
type: String,
required: true
}
});

module.exports = schema;
90 changes: 90 additions & 0 deletions src/services/GithubService.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ const dbHelper = require('../common/db-helper');
const User = require('../models').User;
const GithubUserMapping = require('../models').GithubUserMapping;
const OwnerUserTeam = require('../models').OwnerUserTeam;
const Organisation = require('../models').Organisation;
const errors = require('../common/errors');
const superagent = require('superagent');
const superagentPromise = require('superagent-promise');

const request = superagentPromise(superagent, Promise);

/**
* Ensure the owner user is in database.
Expand Down Expand Up @@ -229,6 +234,89 @@ addTeamMember.schema = Joi.object().keys({
accessLevel: Joi.string().required(),
});

/**
* Add organisation member.
* @param {String} organisation the organisation name
* @param {String} normalUserToken the normal user token
* @returns {Promise} the promise result
*/
async function addOrganisationMember(organisation, normalUserToken) {
let state;
try {
const dbOrganisation = await dbHelper.queryOneOrganisation(Organisation, organisation);
if (!dbOrganisation) {
console.log(`Personal access token not found for organisation ${organisation}.`); /* eslint-disable-line no-console */
return {};
}
const githubNormalUser = new GitHub({
token: normalUserToken,
});
const normalUser = await githubNormalUser.getUser().getProfile();
const username = normalUser.data.login;
const base64PAT = Buffer.from(`${dbOrganisation.owner}:${dbOrganisation.personalAccessToken}`).toString('base64');
const result = await request
.put(`https://api.github.com/orgs/${organisation}/memberships/${username}`)
.send({role: 'member'})
.set('User-Agent', 'superagent')
.set('Accept', 'application/vnd.github.v3+json')
.set('Authorization', `Basic ${base64PAT}`)
.end();
state = _.get(result, 'body.state');
} catch (err) {
// if error is already exists discard
if (_.chain(err).get('body.errors').countBy({
code: 'already_exists',
}).get('true')
.isUndefined()
.value()) {
throw helper.convertGitHubError(err, 'Failed to add organisation member');
}
}
// return its state
return {state};
}

addOrganisationMember.schema = Joi.object().keys({
organisation: Joi.string().required(),
normalUserToken: Joi.string().required()
});

/**
* Accept organisation invitation by member.
* @param {String} organisation the organisation name
* @param {String} normalUserToken the normal user token
* @returns {Promise} the promise result
*/
async function acceptOrganisationInvitation(organisation, normalUserToken) {
let state;
try {
const result = await request
.patch(`https://api.github.com/user/memberships/orgs/${organisation}`)
.send({state: 'active'})
.set('User-Agent', 'superagent')
.set('Accept', 'application/vnd.github.v3+json')
.set('Authorization', `token ${normalUserToken}`)
.end();
state = _.get(result, 'body.state');
} catch (err) {
// if error is already exists discard
if (_.chain(err).get('body.errors').countBy({
code: 'already_exists',
}).get('true')
.isUndefined()
.value()) {
throw helper.convertGitHubError(err, 'Failed to accept organisation invitation');
}
}
// return its state
return {state};
}

acceptOrganisationInvitation.schema = Joi.object().keys({
organisation: Joi.string().required(),
normalUserToken: Joi.string().required()
});

/**
* Gets the user id by username
* @param {string} username the username
Expand Down Expand Up @@ -320,6 +408,8 @@ module.exports = {
getUserIdByUsername,
getTeamDetails,
deleteUserFromGithubTeam,
addOrganisationMember,
acceptOrganisationInvitation
};

helper.buildService(module.exports);