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

OAuth server side functions #96

Merged
merged 30 commits into from Sep 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
069c3bf
add+update user; create\delete webhook
thatkookooguy Aug 4, 2017
15768f2
create two api end-points
thatkookooguy Aug 4, 2017
6a71542
install missing dependency for firebase-admin
thatkookooguy Aug 9, 2017
601e114
Simplify our console service to create defaults (show time and location)
thatkookooguy Aug 9, 2017
5b9cedd
change consoleService call
thatkookooguy Aug 9, 2017
2465f06
change consoleService call + fix status warning + add missing code
thatkookooguy Aug 9, 2017
8be2346
move consoleService into app/models folder
thatkookooguy Aug 9, 2017
d7e2132
implement api functions inside files
thatkookooguy Aug 9, 2017
fa5dbd1
add api file
thatkookooguy Aug 9, 2017
03e3085
use api file inside index.js
thatkookooguy Aug 9, 2017
b1340fc
ignore mocha reports
thatkookooguy Aug 9, 2017
c20e719
NEVER save private config file
thatkookooguy Aug 9, 2017
6aa5827
save and require Q in our project (missing previous commit)
thatkookooguy Aug 9, 2017
a96b583
change order in api so routes won't override other routes
thatkookooguy Aug 9, 2017
4a84fc5
create new configuration service
thatkookooguy Aug 9, 2017
02184df
use new configService + uncomment firebase admin initialization
thatkookooguy Aug 9, 2017
0cc9ea2
put savePrivate inside configurationService
thatkookooguy Aug 10, 2017
0cce711
remove traces of nconf in other files
thatkookooguy Aug 10, 2017
dcfad08
lint
thatkookooguy Aug 10, 2017
273885d
make sure `updateWith` object is defined
thatkookooguy Aug 10, 2017
cf1cbe5
get the firebase admin from userService
thatkookooguy Aug 10, 2017
8123bc8
expose authenticateUsingToken instead of the defaultAuth object
thatkookooguy Aug 11, 2017
598c00d
also use authenticateUsingToken internally for tests
thatkookooguy Aug 11, 2017
cb1e915
basic structure for userService specs
thatkookooguy Aug 11, 2017
80168a2
iif not all firebase admin vars are set, don't authenticate
thatkookooguy Aug 13, 2017
83ac2bb
change userService model structure to support easier tests
thatkookooguy Aug 19, 2017
4649b7c
write mocks to help test individual files
thatkookooguy Aug 19, 2017
a71bfb0
add some tests for userService auth functions
thatkookooguy Aug 19, 2017
fe43507
comment out e2e and check how to fix the tests timing-out
thatkookooguy Aug 19, 2017
16c17fc
Merge branch 'master' into auth-server
thatkookooguy Sep 29, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -7,3 +7,7 @@ coverage/
.idea/

monkeyDB.json

mochawesome-report/

privateConfig.json
217 changes: 206 additions & 11 deletions achievibitDB.js
@@ -1,29 +1,29 @@
var _ = require('lodash');
var nconf = require('nconf');
nconf.argv().env();
var dbLibrary = nconf.get('testDB') ? 'monkey-js' : 'monk';
var Q = require('q');
var configService = require('./app/models/configurationService')();
var CONFIG = configService.get();
var dbLibrary = CONFIG.testDB ? 'monkey-js' : 'monk';
var monk = require(dbLibrary);
var async = require('async');
var utilities = require('./utilities');
var github = require('octonode');
var request = require('request');
var colors = require('colors');
var client = github.client({
username: nconf.get('githubUser'),
password: nconf.get('githubPassword')
username: CONFIG.githubUser,
password: CONFIG.githubPassword
});
var console = require('./consoleService')('achievibitDB', [
'cyan',
'inverse'
], process.console);
var console = require('./app/models/consoleService')();

var url = nconf.get('databaseUrl');
var url = CONFIG.databaseUrl;
var db = monk(url);
var apiUrl = 'https://api.github.com/repos/';

var collections = {
repos: db.get('repos'),
users: db.get('users')
users: db.get('users'),
// uses to store additional private user data
userSettings: db.get('auth_users')
};

var achievibitDB = {};
Expand All @@ -38,6 +38,9 @@ achievibitDB.updatePartialArray = updatePartialArray;
achievibitDB.getExtraPRData = getExtraPRData;
achievibitDB.addPRItems = addPRItems;
achievibitDB.connectUsersAndRepos = connectUsersAndRepos;
achievibitDB.createAchievibitWebhook = createAchievibitWebhook;
achievibitDB.deleteAchievibitWebhook = deleteAchievibitWebhook;
achievibitDB.getAndUpdateUserData = getAndUpdateUserData;

module.exports = achievibitDB;

Expand Down Expand Up @@ -541,6 +544,198 @@ function getReactions(comment) {
};
}

function createAchievibitWebhook(repoName, gToken, uid) {
var githubWebhookConfig = {
name: 'web', //'achievibit',
active: true,
events: [
'pull_request',
'pull_request_review',
'pull_request_review_comment'
],
config: {
'url': 'http://achievibit.kibibit.io/',
'content_type': 'json'
}
};

var creatWebhookUrl = [
apiUrl,
repoName,
'/hooks'
].join('');
request({
method: 'POST',
url: creatWebhookUrl,
headers: {
'User-Agent': 'achievibit',
'Authorization': 'token ' + gToken
},
json: true,
body: githubWebhookConfig
}, function(err, response, body) {
if (err) {
console.error('had a problem creating a webhook for ' + repoName, err);
return;
}

if (response.statusCode === 200 || response.statusCode === 201) {
console.log('webhook added successfully');
var identityObject = {
uid: uid
};
findItem('userSettings', identityObject).then(function(savedUser) {
if (!_.isEmpty(savedUser)) {
savedUser = savedUser[0];
var newIntegrations =
_.map(savedUser.reposIntegration, function(repo) {
if (repo.name === repoName) {
repo.id = body.id;
repo.integrated = true;
}

return repo;
});
updatePartially('userSettings', identityObject, {
'reposIntegration': newIntegrations
});
}
});
} else {
console.error([
'creating webhook: ',
'wrong status from server: ',
'[', response.statusCode, ']'
].join(''), body);
}
});
}

function deleteAchievibitWebhook(repoName, gToken, uid) {
var deleteWebhookUrl = [
apiUrl,
repoName,
'/hooks'
].join('');

var identityObject = {
uid: uid
};

findItem('userSettings', identityObject).then(function(savedUser) {
if (!_.isEmpty(savedUser)) {
savedUser = savedUser[0];
var repoUserData = _.find(savedUser.reposIntegration, {
name: repoName
});

if (!repoUserData.id) {
return 'error!';
}
deleteWebhookUrl += '/' + repoUserData.id;
repoUserData.id = null;
repoUserData.integrated = false;

request({
method: 'DELETE',
url: deleteWebhookUrl,
headers: {
'User-Agent': 'achievibit',
'Authorization': 'token ' + gToken
},
json: true
}, function(err, response, body) {
if (err) {
console.error('had a problem deleting a webhook for ' + repoName,
err);
return;
}

updatePartially('userSettings', identityObject, {
'reposIntegration': savedUser.reposIntegration
});
});
} else {
return 'error!';
}
});
}

function getAndUpdateUserData(uid, updateWith) {
var deferred = Q.defer();
if (_.isNil(uid)) { deferred.reject('expected a uid'); }

// var authUsers = collections.userSettings;
var identityObject = {
uid: uid
};

updateWith = updateWith || {};

findItem('userSettings', identityObject).then(function(savedUser) {
if (!_.isEmpty(savedUser)) {
savedUser = savedUser[0];
if (!_.isEmpty(updateWith)) { // new sign in so update tokens
updatePartially('userSettings', identityObject, updateWith);
}
// we don't wait for the promise here because we already have the new data
// update if needed
savedUser.username = updateWith.username || savedUser.username;
savedUser.githubToken = updateWith.githubToken || savedUser.githubToken;
// return the updated saved user
deferred.resolve(savedUser);
} else { // this is a new user in our database
// we should have this data given from the client if it's a new user,
// but something can go wrong sometimes, so: defaults.
var newUser = {
username: updateWith.username || null,
uid: uid,
signedUpOn: Date.now(),
postAchievementsAsComments:
updateWith.postAchievementsAsComments || true,
reposIntegration: updateWith.reposIntegration || [],
timezone: updateWith.timezone || null,
githubToken: updateWith.githubToken || null
};

// get the user's repos and store them in the user object
var client = github.client(newUser.githubToken);
var ghme = client.me();

ghme.repos(function(err, repos) { // headers
if (err) resolve.reject('couldn\'t fetch repos');
else {
var parsedRepos = [];
_.forEach(repos, function(repo) {
//var escapedRepoName = _.replace(repo.full_name, /\./g, '@@@');
parsedRepos.push({
name: repo.full_name,
integrated: false
});
});
newUser.reposIntegration = parsedRepos;

// test out automatic integration with Thatkookooguy/monkey-js
// createAchievibitWebhook(_.find(repos, {
// 'full_name': 'Thatkookooguy/monkey-js'
// }), newUser.githubToken);

insertItem('userSettings', newUser);
// this is added to the db. create a copy of new user first
var returnedUser = _.clone(newUser);
returnedUser.newUser = true;

deferred.resolve(returnedUser);
}
});
}
}, function(error) {
deferred.reject('something went wrong with searching a user', error);
});

return deferred.promise;
}

function getNewFileFromPatch(patch) {
if (!patch) {
return;
Expand Down
57 changes: 57 additions & 0 deletions app/models/badgeService.js
@@ -0,0 +1,57 @@
var _ = require('lodash');
var badge = require('gh-badges');

var achievements = require('require-all')({
dirname: appRoot + '/achievements',
filter: /(.+achievement)\.js$/,
excludeDirs: /^\.(git|svn)$/,
recursive: true
});

var badgeService = {};

badgeService.get = function(req, res) {
badge.loadFont('./Verdana.ttf', function() {
badge(
{
text: [
'achievements',
_.keys(achievements).length
],
colorA: '#894597',
colorB: '#5d5d5d',
template: 'flat',
logo: [
'data:image/png;base64,iVBORw0KGgoAAAA',
'NSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJL',
'R0QA/wD/AP+gvaeTAAAA/0lEQVRYhe3WMU7DM',
'BjFcadqh0qdWWBl7QU4Ss/AjsREF8RdOhYO0E',
'qoN2DhFIgBOvBjIIMVxSFyUiEhP8lD7C/v/T9',
'7sEMoKkoIe+Npn8qpOgCM2VBVVa1ZkzFDcjQd',
'apDqLIR+u/jnO1AACkABKABdAO9DjHEWfb7lA',
'LwOAQghXPXx6gJ4zE3GJIRwE0095Zhc4PO3iz',
'7x7zoq+cB5bifr9tg0AK7xFZXcZYXXZjNs+wB',
'giofG8hazbIDaeI5dFwAu8dxY2mE+KDyCWGCT',
'YLj3c86xNliMEh5BVLjFseNEjnVN8pU0BsgSh',
'5bwA5YnC25AVFjhpR6rk3Zd9K/1Dcae2pUn6m',
'qiAAAAAElFTkSuQmCC'
].join('')
},
function(svg) {
res.setHeader('Content-Type', 'image/svg+xml;charset=utf-8');
res.setHeader('Pragma-directive', 'no-cache');
res.setHeader('Cache-directive', 'no-cache');
res.setHeader('Pragma','no-cache');
res.setHeader('Expires','0');
// Cache management - no cache,
// so it won't be cached by GitHub's CDN.
res.setHeader('Cache-Control',
'no-cache, no-store, must-revalidate');

res.send(svg);
}
);
});
};

module.exports = badgeService;
73 changes: 73 additions & 0 deletions app/models/configurationService.js
@@ -0,0 +1,73 @@
var _ = require('lodash');
var console = require('./consoleService')();
var nconf = require('nconf');
var auth = require('http-auth'); // @see https://github.com/gevorg/http-auth

var allAchievibitConfigNames = [
'firebaseType',
'firebaseProjectId',
'firebasePrivateKeyId',
'firebasePrivateKey',
'firebaseClientEmail',
'firebaseClientId',
'firebaseAuthUri',
'firebaseTokenUri',
'firebaseAPx509CU',
'firebaseCx509CU',
'port',
'databaseUrl',
'stealth',
'testDB',
'logsUsername',
'logsPassword',
'ngrokToken'
];

// look for config in:
nconf
.argv()
.env({whitelist: allAchievibitConfigNames})
.file({ file: 'privateConfig.json' });

var configService = function() {

var shouldSaveToFile = nconf.get('savePrivate');

if (shouldSaveToFile) {
_.forEach(allAchievibitConfigNames, function(varName) {
nconf.set(varName, nconf.get(varName));
});

nconf.save(function (err) {
if (err) {
console.error('problem saving private configuration');
} else {
console.info('PERSONAL CONFIG SAVED! DELETE WHEN FINISHED!');
}
});
}

return {
get: function(name) {
return nconf.get(name);
},
haveLogsAuth: !_.isNil(nconf.get('logsUsername')),
createLogsAuthForExpress: function() {
var basicAuth = auth.basic({
realm: 'achievibit ScribeJS WebPanel'
}, function (username, password, callback) {
var logsUsername = nconf.get('logsUsername') ?
nconf.get('logsUsername') + '' : '';

var logsPassword = nconf.get('logsPassword') ?
nconf.get('logsPassword') + '' : '';

callback(username === logsUsername && password === logsPassword);
});

return auth.connect(basicAuth);
}
};
};

module.exports = configService;