1 /*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, strict:true, trailing:true, white:true */
3 /*global X:true, _:true, SYS:true */
12 var passport = require('passport'),
16 Receives user authentication credentials and have passport do the authentication.
19 //passport.authenticate('local', { successReturnToOrRedirect: '/login/scope', failureRedirect: '/', failureFlash: 'Invalid username or password.' }),
20 passport.authenticate('local', { failureRedirect: '/?login=fail' }),
21 function (req, res, next) {
22 var pathName = "/app";
23 if (req && req.session && !req.session.oauth2 && req.session.passport && req.session.passport.user && req.session.passport.user.organization) {
24 if (req.body.hash && req.body.hash.charAt(0) === "#") {
25 pathName = pathName + req.body.hash;
27 res.redirect("/" + req.session.passport.user.organization + pathName);
30 exports.scopeForm(req, res, next);
36 Renders the login form
38 exports.loginForm = function (req, res) {
41 if (req.query && req.query.login && req.query.login === 'fail') {
42 message = ["Invalid username or password."];
45 res.render('login', { message: message, databases: X.options.datasource.databases });
49 Logs out user by removing the session and sending the user to the login screen.
51 exports.logout = function (req, res) {
52 if (req.session.passport) {
53 // Make extra sure passport is empty.
54 req.session.passport = null;
58 // Kill the whole session, db, cache and all.
59 req.session.destroy(function () {});
62 if (req.path.split("/")[1]) {
63 res.clearCookie(req.path.split("/")[1] + ".sid");
71 Receives a request telling us which organization a user has selected
72 to log into. Note that we don't trust the client; we check
73 to make sure that the user actually belongs to that organization.
75 exports.scope = function (req, res, next) {
76 var userId = req.session.passport.user.id,
77 selectedOrg = req.body.org,
78 user = new SYS.User(),
81 options.success = function (response) {
86 if (response.length === 0) {
87 if (req.session && req.session.oauth2 && req.session.oauth2.redirectURI) {
88 X.log("OAuth 2.0 User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
89 res.redirect(req.session.oauth2.redirectURI + '?error=access_denied');
93 X.log("User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
94 res.redirect('/' + selectedOrg + '/logout');
96 } else if (response.length > 1) {
97 X.log("More than one User: %@ exists.".f(userId));
98 res.redirect('/' + selectedOrg + '/logout');
102 // We can now trust this user's request to log in to this organization.
104 // Update the session store row to add the org choice and username.
105 // Note: Updating this object magically persists the data into the SessionStore table.
107 //privs = _.map(response.get("privileges"), function (privAss) {
108 // return privAss.privilege.name;
111 //_.each(response.get('organizations'), function (orgValue, orgKey, orgList) {
112 // if (orgValue.name === selectedOrg) {
113 // userOrg = orgValue.name;
114 // userName = orgValue.username;
118 //if (!userOrg || !userName) {
119 if (!response.get("username")) {
120 // This shouldn't happen.
121 X.log("User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
122 res.redirect('/' + selectedOrg + '/logout');
126 //req.session.passport.user.globalPrivileges = privs;
127 req.session.passport.user.organization = response.get("organization");
128 req.session.passport.user.username = response.get("username");
130 // TODO - req.oauth probably isn't enough here, but it's working 2013-03-15...
131 // If this is an OAuth 2.0 login with only 1 org.
136 // If this is an OAuth 2.0 login with more than 1 org.
137 if (req.session.returnTo) {
138 res.redirect(req.session.returnTo);
140 // Redirect to start loading the client app.
141 res.redirect('/' + selectedOrg + '/app');
145 options.error = function (model, error) {
146 X.log("userorg fetch error", error);
147 res.redirect('/' + selectedOrg + '/logout');
152 // The user id we're searching for.
155 // The user under whose authority the query is run.
156 options.username = X.options.databaseServer.user;
157 options.database = selectedOrg;
159 // Verify that the org is valid for the user.
164 Loads the form to let the user choose their organization. If there's only one
165 organization for the user we choose for them.
167 exports.scopeForm = function (req, res, next) {
168 var organizations = [],
173 organizations = _.map(req.user.get("organizations"), function (org) {
177 // Prevent unauthorized access.
182 // If this is an OAuth 2.0 login req, try and get the org from the requested scope.
183 if (req.session && req.session.oauth2) {
185 if (req.session.oauth2.req && req.session.oauth2.req.scope && req.session.oauth2.req.scope.length > 0) {
186 // Loop through the scope URIs and convert them to org names.
187 _.each(req.session.oauth2.req.scope, function (value, key, list) {
190 // Get the org from the scope URI e.g. 'dev' from: 'https://mobile.xtuple.com/auth/dev'
191 scope = url.parse(value, true);
192 org = scope.path.split("/")[1] || null;
194 // TODO - Still need more work to support userinfo calls.
195 // See node-datasource/oauth2/oauth2.js authorization.
197 // The scope 'https://mobile.xtuple.com/auth/userinfo.xxx' can be used to make userinfo
198 // REST calls and is not a valid org scope, we'll skip it here.
199 if (org && org.indexOf('userinfo') === -1) {
204 // If we only have one scope/org sent, choose it for this request.
205 if (scopes.length === 1) {
206 req.body.org = scopes[0];
207 exports.scope(req, res, next);
211 // TODO - Multiple scopes sent.
212 // Do we want to let them select an org or respond with error and scopeList?
213 // It depends on the scenario. Some support user interaction and can select an org, others
214 // do not and should get an error.
218 // TODO - No scope is sent.
219 // Do we want to let them select an org or respond with error and scopeList?
220 // It depends on the scenario. Some support user interaction and can select an org, others
221 // do not and should get an error.
225 // Below will handle OAuth "TODO - Multiple scopes sent", "TODO - No scope is sent." above for now.
227 // Choose an org automatically if there's only one for this user.
228 if (organizations.length === 1) {
229 req.body.org = organizations[0];
230 exports.scope(req, res, next);
234 // Some users may not have any orgs. They should not get this far.
235 if (organizations.length === 0) {
236 X.err("User: %@ shall not pass, they have no orgs to select.".f(req.session.passport.user.id));
237 req.flash('orgerror', 'You have not been assigned to any organizations.');
240 // We've got nothing, let the user choose their scope/org.
241 res.render('scope', { organizations: organizations.sort(), message: req.flash('orgerror') });