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) {
23 if (req && req.session && !req.session.oauth2 && req.session.passport && req.session.passport.user && req.session.passport.user.organization) {
24 res.redirect("/" + req.session.passport.user.organization + '/app');
27 exports.scopeForm(req, res, next);
33 Renders the login form
35 exports.loginForm = function (req, res) {
38 if (req.query && req.query.login && req.query.login === 'fail') {
39 message = ["Invalid username or password."];
42 res.render('login', { message: message, databases: X.options.datasource.databases });
46 Logs out user by removing the session and sending the user to the login screen.
48 exports.logout = function (req, res) {
49 if (req.session.passport) {
50 // Make extra sure passport is empty.
51 req.session.passport = null;
55 // Kill the whole session, db, cache and all.
56 req.session.destroy(function () {});
59 if (req.path.split("/")[1]) {
60 res.clearCookie(req.path.split("/")[1] + ".sid");
68 Receives a request telling us which organization a user has selected
69 to log into. Note that we don't trust the client; we check
70 to make sure that the user actually belongs to that organization.
72 exports.scope = function (req, res, next) {
73 var userId = req.session.passport.user.id,
74 selectedOrg = req.body.org,
75 user = new SYS.User(),
78 options.success = function (response) {
83 if (response.length === 0) {
84 if (req.session && req.session.oauth2 && req.session.oauth2.redirectURI) {
85 X.log("OAuth 2.0 User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
86 res.redirect(req.session.oauth2.redirectURI + '?error=access_denied');
90 X.log("User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
91 res.redirect('/' + selectedOrg + '/logout');
93 } else if (response.length > 1) {
94 X.log("More than one User: %@ exists.".f(userId));
95 res.redirect('/' + selectedOrg + '/logout');
99 // We can now trust this user's request to log in to this organization.
101 // Update the session store row to add the org choice and username.
102 // Note: Updating this object magically persists the data into the SessionStore table.
104 //privs = _.map(response.get("privileges"), function (privAss) {
105 // return privAss.privilege.name;
108 //_.each(response.get('organizations'), function (orgValue, orgKey, orgList) {
109 // if (orgValue.name === selectedOrg) {
110 // userOrg = orgValue.name;
111 // userName = orgValue.username;
115 //if (!userOrg || !userName) {
116 if (!response.get("username")) {
117 // This shouldn't happen.
118 X.log("User %@ has no business trying to log in to organization %@.".f(userId, selectedOrg));
119 res.redirect('/' + selectedOrg + '/logout');
123 //req.session.passport.user.globalPrivileges = privs;
124 req.session.passport.user.organization = response.get("organization");
125 req.session.passport.user.username = response.get("username");
127 // TODO - req.oauth probably isn't enough here, but it's working 2013-03-15...
128 // If this is an OAuth 2.0 login with only 1 org.
133 // If this is an OAuth 2.0 login with more than 1 org.
134 if (req.session.returnTo) {
135 res.redirect(req.session.returnTo);
137 // Redirect to start loading the client app.
138 res.redirect('/' + selectedOrg + '/app');
142 options.error = function (model, error) {
143 X.log("userorg fetch error", error);
144 res.redirect('/' + selectedOrg + '/logout');
149 // The user id we're searching for.
152 // The user under whose authority the query is run.
153 options.username = X.options.databaseServer.user;
154 options.database = selectedOrg;
156 // Verify that the org is valid for the user.
161 Loads the form to let the user choose their organization. If there's only one
162 organization for the user we choose for them.
164 exports.scopeForm = function (req, res, next) {
165 var organizations = [],
170 organizations = _.map(req.user.get("organizations"), function (org) {
174 // Prevent unauthorized access.
179 // If this is an OAuth 2.0 login req, try and get the org from the requested scope.
180 if (req.session && req.session.oauth2) {
182 if (req.session.oauth2.req && req.session.oauth2.req.scope && req.session.oauth2.req.scope.length > 0) {
183 // Loop through the scope URIs and convert them to org names.
184 _.each(req.session.oauth2.req.scope, function (value, key, list) {
187 // Get the org from the scope URI e.g. 'dev' from: 'https://mobile.xtuple.com/auth/dev'
188 scope = url.parse(value, true);
189 org = scope.path.split("/")[1] || null;
191 // TODO - Still need more work to support userinfo calls.
192 // See node-datasource/oauth2/oauth2.js authorization.
194 // The scope 'https://mobile.xtuple.com/auth/userinfo.xxx' can be used to make userinfo
195 // REST calls and is not a valid org scope, we'll skip it here.
196 if (org && org.indexOf('userinfo') === -1) {
201 // If we only have one scope/org sent, choose it for this request.
202 if (scopes.length === 1) {
203 req.body.org = scopes[0];
204 exports.scope(req, res, next);
208 // TODO - Multiple scopes sent.
209 // Do we want to let them select an org or respond with error and scopeList?
210 // It depends on the scenario. Some support user interaction and can select an org, others
211 // do not and should get an error.
215 // TODO - No scope is sent.
216 // Do we want to let them select an org or respond with error and scopeList?
217 // It depends on the scenario. Some support user interaction and can select an org, others
218 // do not and should get an error.
222 // Below will handle OAuth "TODO - Multiple scopes sent", "TODO - No scope is sent." above for now.
224 // Choose an org automatically if there's only one for this user.
225 if (organizations.length === 1) {
226 req.body.org = organizations[0];
227 exports.scope(req, res, next);
231 // Some users may not have any orgs. They should not get this far.
232 if (organizations.length === 0) {
233 X.err("User: %@ shall not pass, they have no orgs to select.".f(req.session.passport.user.id));
234 req.flash('orgerror', 'You have not been assigned to any organizations.');
237 // We've got nothing, let the user choose their scope/org.
238 res.render('scope', { organizations: organizations.sort(), message: req.flash('orgerror') });