Skip to content
Merged
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
17 changes: 17 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ global.app.use(require('compression')());

// Cookies
global.app.use(require('cookie-parser')());
global.app.use(require('express-session')({
secret: (function() {
var d = new Date().getTime();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
Copy link
Member

Choose a reason for hiding this comment

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

Like

});
})(),
resave: false,
saveUninitialized: true
}));

app.use(function (req, res, next) {
res.cookie('source-mode', global.MODE, { maxAge: 3600000, httpOnly: false });

Expand All @@ -71,6 +84,10 @@ app.use(bodyParser.json());

/* Middlewares */

// Auth initializing
var auth = require('./core/auth')(app);
app.use(auth.everyauth.middleware());

// LESS processing
if (global.MODE === 'development') {
var less = require('less-middleware');
Expand Down
1 change: 1 addition & 0 deletions assets/css/defaults.less
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
@import url('project/modal.less');
@import url('project/autocomplete.less');
@import url('project/clarify-in-spec.less');
@import url('project/auth.less');

//Cosmetic
@import url('cosmetic/highlights.less');
Expand Down
35 changes: 35 additions & 0 deletions assets/css/project/auth.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.source_login {
display: inline-block;
height: 40px;
float: right;

margin-top: 2px;
vertical-align: top;
}

.source_login-avatar {
width: 25px;
height: 25px;

border-radius: 50%;
border: 1px solid #FFF;
cursor: pointer;
}

.source_login-avatar.anonymous {
border: 1px solid #333;
}

.source_login-button {
display: inline-block;

color: #999;
text-decoration: none;
line-height: 1;
cursor: pointer;
vertical-align: top;
margin: 5px 0px 0px 10px;
}
.source_login-button:hover {
color: #FFF
}
Binary file added assets/i/github-loagind.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/i/unknown.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 8 additions & 3 deletions assets/js/enter-the-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ require([
"jquery",
"source/load-options", // TODO: remove when all modules inherit Module()
"sourceModules/browser",
"sourceModules/moduleLoader"
], function ($, options) {

"sourceModules/moduleLoader",
'sourceModules/auth',
], function ($, options, browser, Loader, Auth) {
if (options && options.modulesEnabled && options.modulesEnabled.auth === true) {
new Auth({
target: $('.js-hook.source_login')
});
}
});
155 changes: 155 additions & 0 deletions assets/js/modules/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
define([
'jquery',
'sourceModules/module'
], function($, Module) {

'use strict';

/**
* @Object default module option values
*/
var defaults = {
'storageKey': 'sourcejsUser',
'defaultAvatarURL': '/source/assets/i/unknown.gif',
'classes': {
'controlsWrapper': 'source_login',
'loginButton': 'source_login-button',
'avatar': 'source_login-avatar',
'anonymous': 'anonymous',
'hook': 'js-hook'
},
'labels': {
'login': 'Login',
'logout': 'Logout'
}
};

/**
* @module Auth - basic GitHub authorization module.
*
* @constructor
*
* @param [Object] config - auth inline configuration set of options
*
* @param [Object] config.target - jquery domElement which goes to be auth controlls container
*
* @param [Object] config.options - options set, which allows to define component configuration.
*/

function Auth(config) {
var _this = this;

this.conf = $.extend(true, {},
Copy link
Member

Choose a reason for hiding this comment

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

It must be possible to override config from global optionjs.js.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

defaults,
config.options,
Copy link
Member

Choose a reason for hiding this comment

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

I think config options must have more priority than user overrides.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so. This config is common for entire project (for team), options is used to customize local instance (single user).

this.options.modulesOptions.auth
);

this.target = config.target || $(this.conf.classes.hook);
if(!$(this.target).hasClass(this.conf.classes.controlsWrapper)) {
$(this.target).addClass(this.conf.classes.controlsWrapper);
}
this.popup;

$(function() {
_this.init();
});
}

Auth.prototype = Module.createInstance();
Auth.prototype.constructor = Auth;

Auth.renderers = {
Copy link
Member

Choose a reason for hiding this comment

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

Why do you leave renderers private? It could be useful to override them in some cases.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just static. the same renderers for all instances. It would be changed then we will use new base module.

'avatar': function() {
var hasAvatar = this.user && this.user.avatar_url;
this.target.append($([
'<img class="', this.conf.classes.avatar, ' ',
(hasAvatar ? '' : this.conf.classes.anonymous), '" src="',
(hasAvatar ? this.user.avatar_url : this.conf.defaultAvatarURL),
'">'
].join('')
));
},
'loginButton': function() {
this.target.append($([
'<div class="', this.conf.classes.loginButton, '">',
(this.user && this.user.id ? this.conf.labels.logout : this.conf.labels.login),
'</div>'
].join('')
));
}
};

/**
* @method Auth.login.
* This function initiates logging in process and creates github login popup.
*/
Auth.prototype.login = function() {
this.popup = open('/auth/stub', 'popup', 'width=1015,height=500');
};

/**
* @method Auth.logout.
* This method removes existed user entity and refreshes control.
*/
Auth.prototype.logout = function() {
localStorage.removeItem(this.conf.storageKey);
this.render();
};

/**
* @method Auth.isLoginned User state getter.
*
* @returns {Boolean} isLoginned. It returns true if user is loginned.
*/
Auth.prototype.isLoginned = function() {
return !!localStorage[this.conf.storageKey];
};

/**
* @method Auth.getUser GitHub user entity getter.
*
* @returns {Object} user || null.
* If user is loginned it returns user object and null in other case.
*/
Auth.prototype.getUser = function() {
return this.isLoginned()
? JSON.parse(localStorage.getItem(this.conf.storageKey))
: null;
};

Auth.prototype.init = function() {
window.sourcejs = window.sourcejs || {};
var self = window.sourcejs.__auth = this;
this.render();
$('body').on('click', '.' + this.conf.classes.loginButton, function(e) {
e.preventDefault();
if (self.isLoginned()) {
self.logout();
} else {
self.login();
}
});
};

Auth.prototype.render = function() {
var user = this.getUser();
var self = this;
this.target.html('');
Object.keys(Auth.renderers).forEach(function(name) {
Auth.renderers[name].call({
'target': self.target,
'user': user,
'conf': self.conf
});
});
};

Auth.prototype.done = function(user) {
this.popup.close();
localStorage.setItem(this.conf.storageKey, JSON.stringify(user));
this.render();
};

return Auth;
});
2 changes: 1 addition & 1 deletion assets/templates/header.inc.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<li class="source_header_nav_i"><a class="source_a_l" href="#777">Link</a></li>
</ul>
</nav>
<a class="js-hook source_login"></a>
</div>
</div>

</header>
85 changes: 85 additions & 0 deletions core/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
var everyauth = require('everyauth');
var fs = require('fs');
var ejs = require('ejs');
var path = require('path');

module.exports = function(app) {
"use strict";

app.states = app.states || {};
app.states.users = app.states.users || {};

var currentUserId = "";

// users data processing
/**
* @method getUser - user getter
*
* @param {String} id - github user id
*
* @returns {Object} - user - github user entity or empty object, if user is undefined
*/
var getUser = function(id) {
Copy link
Member

Choose a reason for hiding this comment

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

I want this methods globally available, for plugins and other modules.

return app.states.users[id] || {};
};

/**
* @method setUser - user setter
*
* @param {Object} user - github user entity
*
* @param {String} user.id - required user field, which is used as user templral storage key.
*
* @returns {Object} user - returns user parameter
*/
var setUser = function(user) {
if (typeof user !== "object" || !user.id) return;
app.states.users[user.id] = user;
return user;
};

everyauth.everymodule.findUserById(function(id, callback) {
callback(null, getUser(id));
});

// TODO: separated id & secret for dev mode
everyauth.github
.appId(global.opts.github.appId)
.appSecret(global.opts.github.appSecret)
.findOrCreateUser(function(sess, accessToken, accessTokenExtra, ghUser) {
setUser(ghUser);
currentUserId = ghUser.id;
return ghUser;
})
.redirectPath('/auth/done');


everyauth.everymodule.handleLogout( function (req, res) {
delete req.session.authCache;
req.logout();
this.redirect(res, this.logoutRedirectPath());
});

// application routes
var authTemplate = fs.readFileSync(path.join(global.pathToApp, '/core/views/auth-done.ejs'), "utf8");
app.get('/auth/stub', function (req, res) {
res.send(require('ejs').render(authTemplate, {
'user': JSON.stringify({})
}));
});

app.get('/auth/done', function (req, res) {
req.session.authCache = req.session.auth;

res.send(ejs.render(authTemplate, {
'user': JSON.stringify(getUser(currentUserId))
}));
});

return {
'getUser': getUser,
'setUser': setUser,
'everyauth': everyauth
};

};
25 changes: 25 additions & 0 deletions core/views/auth-done.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<head>
<title>Github OAuth</title>
<style type="text/css">
body { overflow: hidden; margin: 0; text-align: center; }
</style>
</head>
<body>
<img src="/source/assets/i/github-loagind.gif" width="500" alt="Loading">
<script type="text/javascript">
window.addEventListener('load', function() {
var data;
try {
data = JSON.parse("<%= user %>".split("&quot;").join('"'));
} catch(e) {
console.log('incorrect user data', e);
}
if (!data instanceof Object || !data.id) {
window.location = "/auth/github";
} else {
opener.sourcejs.__auth.done(data);
}
});
</script>
</body>
Loading