diff --git a/app/client/src/modules/app/index.js b/app/client/src/modules/app/index.js index d6c33dc..73c9f0c 100755 --- a/app/client/src/modules/app/index.js +++ b/app/client/src/modules/app/index.js @@ -20,6 +20,19 @@ module.exports = //load other app modules here, e.g.: //require('./account').name ]) - .config(function ($urlRouterProvider) { + .config(function ($urlRouterProvider, $httpProvider) { $urlRouterProvider.otherwise('/'); + $httpProvider.interceptors.push('authInterceptor'); + }).factory('authInterceptor', function ($q, $location) { + return { + responseError(response) { + if (response.status === 401) { + console.log('redirecting to login'); + $location.path('/login'); + //$cookieStore.remove('token'); + } + + return $q.reject(response); + } + }; }); diff --git a/app/client/src/modules/app/main/login.html b/app/client/src/modules/app/main/login.html new file mode 100644 index 0000000..e1d8a19 --- /dev/null +++ b/app/client/src/modules/app/main/login.html @@ -0,0 +1 @@ +Login with GitHub \ No newline at end of file diff --git a/app/client/src/modules/app/main/main.js b/app/client/src/modules/app/main/main.js index 27281b6..459f635 100644 --- a/app/client/src/modules/app/main/main.js +++ b/app/client/src/modules/app/main/main.js @@ -8,6 +8,10 @@ module.exports = url: '/', templateUrl: 'app/main/main.html', controller: 'mainController as ctrl' + }) + .state('login', { + url: '/login', + templateUrl: 'app/main/login.html' }); }) .controller('mainController', function () { diff --git a/app/server/apps/repos/index.js b/app/server/apps/repos/index.js index 15ecfa7..ebf35c4 100644 --- a/app/server/apps/repos/index.js +++ b/app/server/apps/repos/index.js @@ -2,6 +2,7 @@ /** * Business logic routes for working with repos. */ + var express = require('express'), jefferson = require('express-jefferson'), conf = require('./routes'), diff --git a/app/server/apps/repos/routes.js b/app/server/apps/repos/routes.js index 509b928..61b43c6 100644 --- a/app/server/apps/repos/routes.js +++ b/app/server/apps/repos/routes.js @@ -1,5 +1,5 @@ 'use strict'; -var auth = require('../../middleware/auth'), +var auth = require('../../middleware/authenticate'), repos = require('../../middleware/repos'), send = require('../../middleware/send'), permissions = require('../../middleware/permissions'); @@ -10,7 +10,7 @@ module.exports = { method: 'GET', path: '/repos', middleware: [ - auth.authenticate, + auth.isAuthenticated, repos.listRepos, repos.listReposPermission, repos.listReposLinks, @@ -21,7 +21,7 @@ module.exports = { method: 'PUT', path: '/repos/:id/users/:username/permissions/:permission', middleware: [ - auth.authenticate, + auth.isAuthenticated, permissions.editRepoPermissionForUser, send.noContent ] diff --git a/app/server/apps/users/index.js b/app/server/apps/users/index.js index 15ecfa7..ebf35c4 100644 --- a/app/server/apps/users/index.js +++ b/app/server/apps/users/index.js @@ -2,6 +2,7 @@ /** * Business logic routes for working with repos. */ + var express = require('express'), jefferson = require('express-jefferson'), conf = require('./routes'), diff --git a/app/server/apps/users/routes.js b/app/server/apps/users/routes.js index 5588c7c..ae73a13 100644 --- a/app/server/apps/users/routes.js +++ b/app/server/apps/users/routes.js @@ -1,5 +1,5 @@ 'use strict'; -var auth = require('../../middleware/auth'), +var auth = require('../../middleware/authenticate'), users = require('../../middleware/users'), permissions = require('../../middleware/permissions'), send = require('../../middleware/send'); @@ -9,7 +9,7 @@ module.exports = { method: 'GET', path: '/users', middleware: [ - auth.authenticate, + auth.isAuthenticated, users.listUsers, users.listUsersPermission, users.listUsersLinks, @@ -20,7 +20,7 @@ module.exports = { method: 'PUT', path: '/users/:username/repos/:id/permissions/:permission', middleware: [ - auth.authenticate, + auth.isAuthenticated, permissions.editRepoPermissionForUser, send.noContent ] diff --git a/app/server/components/repositories/users.js b/app/server/components/repositories/users.js index efd58c8..97a6ca7 100644 --- a/app/server/components/repositories/users.js +++ b/app/server/components/repositories/users.js @@ -1,7 +1,8 @@ 'use strict'; var userUtil = require('./util/users'), - Bluebird = require('bluebird'); + Bluebird = require('bluebird'), + permUtil = require('./util/permissions'); module.exports = { @@ -10,6 +11,11 @@ module.exports = { let profiles = users.map(user => userUtil.getGithubUser(user.username).then(profile => user.name = profile.name)); return Bluebird.all(profiles).then(() => users); }); - } + }, + isOrgMember (username) { + return userUtil.isOrgMember(username).then(function (data) { + return "204 No Content" === data.meta.success; + }); + } }; diff --git a/app/server/components/repositories/util/users.js b/app/server/components/repositories/util/users.js index 5f3ec69..9d6e9da 100644 --- a/app/server/components/repositories/util/users.js +++ b/app/server/components/repositories/util/users.js @@ -5,7 +5,6 @@ var provider = require('./provider'), convertGithubUser; convertGithubUser = (user) => { - console.log('user', user); return { username: user.login, name: user.name, @@ -24,5 +23,12 @@ module.exports = { getGithubUsers() { var args = provider.getDefaultListArgs(); return provider.github.getUsers(args).then(users => users.map(user => convertGithubUser(user))); + }, + + isOrgMember(username) { + return provider.github.isOrgMember({ + org: provider.github.config.org, + username: username + }); } }; diff --git a/app/server/components/services/github.js b/app/server/components/services/github.js index 8da2fbe..0868746 100644 --- a/app/server/components/services/github.js +++ b/app/server/components/services/github.js @@ -34,6 +34,7 @@ module.exports = { config: { org: org }, + isOrgMember: Bluebird.promisify(github.orgs.getMember), getUsers: Bluebird.promisify(github.orgs.getMembers), getUser: Bluebird.promisify(github.user.getFrom), getRepos: Bluebird.promisify(github.repos.getFromOrg), diff --git a/app/server/components/services/github.mock.js b/app/server/components/services/github.mock.js index feb5cd0..d64ea91 100644 --- a/app/server/components/services/github.mock.js +++ b/app/server/components/services/github.mock.js @@ -201,5 +201,13 @@ module.exports = { reject(new Error('No mock team: ' + id)); } }); + }, + + isOrgMember (msg) { + return new Promise((resolve, reject) => { + resolve({ meta: { + success: "204 No Content" + }}); + }); } }; diff --git a/app/server/main.js b/app/server/main.js index 26c7825..6d4ce9a 100644 --- a/app/server/main.js +++ b/app/server/main.js @@ -2,15 +2,43 @@ exports.start = () => { - var express = require('express'), + // HACKING IN CONFIG OBJECT HERE + var config = { + server: { + port: '3000', + api_prefix: '/api/v1', + hostname: 'localhost' + }, + github: { + clientID: process.env.GITHUB_CLIENTID, + clientSecret: process.env.GITHUB_CLIENT_KEY, + authRoute: '/auth/github', + authCallbackRoute: '/auth/github/callback', + failureCallback: '/auth/failure' + }, + session: { + secret: process.env.SESSION_SECRET || 'keyboard cat', + resave: false, + saveUninitialized: true, + cookie: { + secure: false + } + }}, + express = require('express'), mountie = require('express-mountie'), http = require('http'), path = require('path'), + session = require('express-session'), + passport = require('./passport'), app = express(); - app.set('port', 3000); + app.use(session(config.session)); + passport.setup(app, config); + + app.set('port', config.server.port); app.use(express.static(path.resolve(__dirname, '../../app/client/build'))); + mountie({ parent: app, src: path.join(__dirname, 'apps'), diff --git a/app/server/middleware/authenticate.js b/app/server/middleware/authenticate.js new file mode 100644 index 0000000..96e9207 --- /dev/null +++ b/app/server/middleware/authenticate.js @@ -0,0 +1,15 @@ +'use strict'; + +var users = require('../components/repositories/users'); + +module.exports = { + isAuthenticated (req, res, next) { + var authenticated = req.isAuthenticated(); + + if (authenticated && users.isOrgMember(req.session.passport.user.username)) { + next(); + } else { + res.send(401); + } + } +}; diff --git a/app/server/middleware/repos.js b/app/server/middleware/repos.js index dbe0c29..f17dc3b 100644 --- a/app/server/middleware/repos.js +++ b/app/server/middleware/repos.js @@ -37,7 +37,7 @@ module.exports = { let repos = req.entity, user = req.query.permission_user, - username = req.auth.username; + username = req.session.passport.user.username; if (user) { permissionRepository.getRepoPermissionsForUser(repos, username).then(permissions => { diff --git a/app/server/middleware/users.js b/app/server/middleware/users.js index cca8c6d..b34b575 100644 --- a/app/server/middleware/users.js +++ b/app/server/middleware/users.js @@ -37,7 +37,7 @@ module.exports = { let repoId = req.query.permission_repo, users = req.entity, - username = req.auth.username; + username = req.session.passport.user.username; if (repoId) { permissionRepository.getUserPermissionForRepo(username, repoId).then(permission => { diff --git a/app/server/mock-passport-middleware.js b/app/server/mock-passport-middleware.js new file mode 100644 index 0000000..ef4b9c6 --- /dev/null +++ b/app/server/mock-passport-middleware.js @@ -0,0 +1,27 @@ +'use strict'; + +//jscs:disable disallowDanglingUnderscores +module.exports = { + initialize(mockUser) { + return function (req, res, next) { + var passport = {}; + passport._key = 'passport'; + passport._userProperty = 'user'; + passport.serializeUser = (user, req, done) => { + done(null, user); + }; + passport.deserializeUser = (user, req, done) => { + done(null, user); + }; + + req._passport = { instance: passport }; + req._passport.session = { user: mockUser }; + req.session.passport = { user: mockUser }; + + next(); + }; + }, + + // TODO ... PUT Mock users in a seperate file so we can tests users that are + mockUser: { username: "TestUser", displayName: "Test User", id: 1 } +}; diff --git a/app/server/passport.js b/app/server/passport.js new file mode 100644 index 0000000..ccb8526 --- /dev/null +++ b/app/server/passport.js @@ -0,0 +1,70 @@ +'use strict'; + + +var passport = require('passport'), + GitHubStrategy = require('passport-github').Strategy, + debug = require('debug')('app:passport'); + +module.exports = { + /** + * Sets up passport-github for authentication by creating routes on the app for retrieving oauth tokens from + * github. + * + * @param app the app that will use the githug authentication + * @param config configuration from the app using the authenticator + */ + setup: function (app, config) { + passport.use(new GitHubStrategy({ + clientID: config.github.clientID, + clientSecret: config.github.clientSecret, + callbackURL: "http://" + config.server.hostname + ":" + config.server.port + config.github.authCallbackRoute + }, + function (accessToken, refreshToken, profile, done) { + done(null, { username: profile.username, displayName: profile.displayName, id: profile.id, token: accessToken }); + })); + + passport.serializeUser(function (user, done) { + debug("serialize user"); + debug(user); + done(null, user); + }); + + passport.deserializeUser(function (user, done) { + debug("deserialize user"); + debug(user); + done(null, user); + }); + + if (process.env.SERVICE === 'mock') { + // TODO ... is there a real di way to do this?? + console.log('using the mock passport middlware'); + var mock = require('./mock-passport-middleware'); + app.use(mock.initialize(mock.mockUser)); + } else { + app.use(passport.initialize()); + } + app.use(passport.session()); + + app.get('/auth/authenticated', function (req, res) { + var authenticated = req.isAuthenicated(); + if (authenticated) { + res.send(200); + } else { + res.send(401); + } + }); + app.get(config.github.authRoute, passport.authenticate('github')); + app.get(config.github.authCallbackRoute, passport.authenticate('github', + { failureRedirect: config.github.failureRedirect, session: true }), + function (req, res) { + var authenticated = req.isAuthenticated(); + debug("authenticated? " + authenticated); + // TODO: redirect to initial request location... + res.redirect('/'); + } + ); + app.get(config.github.failureCallback, function (req, res, next) { + res.send(401); + }); + } +}; diff --git a/package.json b/package.json index e290e14..fc16f99 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,13 @@ "bluebird": "^2.9.13", "debug": "^2.1.3", "express": "^4.12.0", + "express-session": "^1.10.3", "express-jefferson": "^1.0.0", "express-mountie": "^3.0.0", "github": "^0.2.3", - "json-mask": "0.3.4" + "json-mask": "0.3.4", + "passport": "^0.2.1", + "passport-github": "^0.1.5" }, "devDependencies": { "babelify": "^5.0.4",