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 */
13 var passport = require('passport'),
17 Receives user authentication credentials and have passport do the authentication.
20 //passport.authenticate('local', { successReturnToOrRedirect: '/login/scope', failureRedirect: '/', failureFlash: 'Invalid username or password.' }),
21 passport.authenticate('local', { failureRedirect: '/?login=fail' }),
22 function (req, res, next) {
24 if (req && req.session && !req.session.oauth2 && req.session.passport && req.session.passport.user && req.session.passport.user.organization) {
25 res.redirect("/" + req.session.passport.user.organization + '/app');
28 exports.scopeForm(req, res, next);
34 Renders the login form
36 exports.loginForm = function (req, res) {
39 if (req.query && req.query.login && req.query.login === 'fail') {
40 message = ["Invalid username or password."];
43 res.render('login', { message: message, databases: X.options.datasource.databases });
47 Logs out user by removing the session and sending the user to the login screen.
49 exports.logout = function (req, res) {
50 if (req.session.passport) {
51 // Make extra sure passport is empty.
52 req.session.passport = null;
56 // Kill the whole session, db, cache and all.
57 req.session.destroy(function () {});
60 if (req.path.split("/")[1]) {
61 res.clearCookie(req.path.split("/")[1] + ".sid");
69 Receives a request telling us which organization a user has selected
70 to log into. Note that we don't trust the client; we check
71 to make sure that the user actually belongs to that organization.
73 exports.scope = function (req, res, next) {
74 var userId = req.session.passport.user.id,
75 selectedOrg = req.body.org,
76 user = new SYS.User(),
79 options.success = function (response) {
84 if (response.length === 0) {
85 if (req.session && req.session.oauth2 && req.session.oauth2.redirectURI) {
86 X.log("OAuth 2.0 User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
87 res.redirect(req.session.oauth2.redirectURI + '?error=access_denied');
91 X.log("User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
92 res.redirect('/' + selectedOrg + '/logout');
94 } else if (response.length > 1) {
95 X.log("More than one User: %@ exists.".f(userId));
96 res.redirect('/' + selectedOrg + '/logout');
100 // We can now trust this user's request to log in to this organization.
102 // Update the session store row to add the org choice and username.
103 // Note: Updating this object magically persists the data into the SessionStore table.
105 //privs = _.map(response.get("privileges"), function (privAss) {
106 // return privAss.privilege.name;
109 //_.each(response.get('organizations'), function (orgValue, orgKey, orgList) {
110 // if (orgValue.name === selectedOrg) {
111 // userOrg = orgValue.name;
112 // userName = orgValue.username;
116 //if (!userOrg || !userName) {
117 if (!response.get("username")) {
118 // This shouldn't happen.
119 X.log("User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
120 res.redirect('/' + selectedOrg + '/logout');
124 //req.session.passport.user.globalPrivileges = privs;
125 req.session.passport.user.organization = response.get("organization");
126 req.session.passport.user.username = response.get("username");
128 // TODO - req.oauth probably isn't enough here, but it's working 2013-03-15...
129 // If this is an OAuth 2.0 login with only 1 org.
134 // If this is an OAuth 2.0 login with more than 1 org.
135 if (req.session.returnTo) {
136 res.redirect(req.session.returnTo);
138 // Redirect to start loading the client app.
139 res.redirect('/client');
143 options.error = function (model, error) {
144 X.log("userorg fetch error", error);
145 res.redirect('/' + selectedOrg + '/logout');
150 // The user id we're searching for.
153 // The user under whose authority the query is run.
154 options.username = X.options.databaseServer.user;
155 options.database = selectedOrg;
157 // Verify that the org is valid for the user.
162 Loads the form to let the user choose their organization. If there's only one
163 organization for the user we choose for them.
165 exports.scopeForm = function (req, res, next) {
166 var organizations = [],
171 organizations = _.map(req.user.get("organizations"), function (org) {
175 // Prevent unauthorized access.
180 // If this is an OAuth 2.0 login req, try and get the org from the requested scope.
181 if (req.session && req.session.oauth2) {
183 if (req.session.oauth2.req && req.session.oauth2.req.scope && req.session.oauth2.req.scope.length > 0) {
184 // Loop through the scope URIs and convert them to org names.
185 _.each(req.session.oauth2.req.scope, function (value, key, list) {
188 // Get the org from the scope URI e.g. 'dev' from: 'https://mobile.xtuple.com/auth/dev'
189 scope = url.parse(value, true);
190 org = scope.path.split("/")[1] || null;
192 // TODO - Still need more work to support userinfo calls.
193 // See node-datasource/oauth2/oauth2.js authorization.
195 // The scope 'https://mobile.xtuple.com/auth/userinfo.xxx' can be used to make userinfo
196 // REST calls and is not a valid org scope, we'll skip it here.
197 if (org && org.indexOf('userinfo') === -1) {
202 // If we only have one scope/org sent, choose it for this request.
203 if (scopes.length === 1) {
204 req.body.org = scopes[0];
205 exports.scope(req, res, next);
209 // TODO - Multiple scopes sent.
210 // Do we want to let them select an org or respond with error and scopeList?
211 // It depends on the scenario. Some support user interaction and can select an org, others
212 // do not and should get an error.
216 // TODO - No scope is sent.
217 // Do we want to let them select an org or respond with error and scopeList?
218 // It depends on the scenario. Some support user interaction and can select an org, others
219 // do not and should get an error.
223 // Below will handle OAuth "TODO - Multiple scopes sent", "TODO - No scope is sent." above for now.
225 // Choose an org automatically if there's only one for this user.
226 if (organizations.length === 1) {
227 req.body.org = organizations[0];
228 exports.scope(req, res, next);
232 // Some users may not have any orgs. They should not get this far.
233 if (organizations.length === 0) {
234 X.err("User: %@ shall not pass, they have no orgs to select.".f(req.session.passport.user.id));
235 req.flash('orgerror', 'You have not been assigned to any organizations.');
238 // We've got nothing, let the user choose their scope/org.
239 res.render('scope', { organizations: organizations.sort(), message: req.flash('orgerror') });