1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | 1× 1× 1× 1× 1× 1× 1× 1× 80× 80× 80× 80× 2× 78× 12× 6× 6× 6× 6× 6× 6× 6× 66× 1× 80× 80× | 'use strict'; var forms = require('../forms'); var collectFormErrors = require('./collect-form-errors'); var isRootDomainMultiTenantRequest = require('./is-root-domain-multi-tenant-request'); var parentDomainRedirect = require('./parent-domain-redirect'); var redirectToOrganization = require('./redirect-to-organization'); var render = require('./render'); var requiresOrganizationResolution = require('./requires-organization-resolution'); /** * Handles the organization resolving process for multi-tenancy. * * Calls that would require organization resolutions (i.e. are made on a sub-domain, * in an application set up to support multi-tenancy) will be redirected to the * parent domain for organization selection. * * Calls that are made to the root domain will handle the showing or processing * of the organization name input form, depending on the request method. When * handled, the processing will be handed over the form the user was originally * trying to access (login, register, password refresh or e-mail verification flow). * * When no processing is needed, the control will be handed to the `next` callback * without any processing. * * @method * @name defaultOrganizationResolver * * @param {Object} req - HTTP request * @param {Object} res - HTTP response * @param {String} formActionUri - URI pointing where to redirect the user to * continue their authorization flow after the organization processing flow is * completed * @param {Function} next - The callback to invoke if (and only if) no multi-tenancy * processing is needed. Never invoked if the this handler does some processing. */ function defaultOrganizationResolver(req, res, formActionUri, next) { var stormpathClient = req.app.get('stormpathClient'); var config = req.app.get('stormpathConfig'); var organizationSelectFormModel = { fields: [{ name: 'organizationNameKey', enabled: null, visible: true, label: config.web.organizationSelect.form.fields.organizationNameKey.label, placeholder: config.web.organizationSelect.form.fields.organizationNameKey.placeholder, required: true, type: 'text' }] }; if (requiresOrganizationResolution(req)) { return parentDomainRedirect(req, res); } if (isRootDomainMultiTenantRequest(req)) { if (req.method === 'GET') { return render(req, res, 'organization-select', { form: forms.organizationSelectForm, formActionUri: formActionUri, formModel: organizationSelectFormModel }); } return forms.organizationSelectForm.handle(req, { success: function (form) { stormpathClient.getOrganizations({nameKey: form.data.organizationNameKey}, function (err, collection) { Iif (err) { return res.json(err); } Iif (collection.items.length !== 1) { return render(req, res, config.web.organizationSelect.view, { form: form, formActionUri: formActionUri, formModel: organizationSelectFormModel, error: 'Organization could not be bound' }); } var organization = collection.items[0]; redirectToOrganization(req, res, organization); }); }, // If we get here, it means the user didn't supply required form fields. error: function (form) { render(req, res, 'organization-select', { form: form, formActionUri: formActionUri, formModel: organizationSelectFormModel, formErrors: collectFormErrors(form) }); }, // If we get here, it means the user is doing a simple GET request, so we // should just render the original template. empty: function (form) { render(req, res, 'organization-select', { form: form, formActionUri: formActionUri, formModel: organizationSelectFormModel }); } }); } next(); } /** * Retrieves an organization resolver to use for multi-tenant authorization. It * will use a resolver registered in the configuration as * `web.multiTenancy.organizationResolver`, if one is defined, and is a function. * Otherwise, the {@link defaultOrganizationResolver} is returned. * * This function must adhere to the following contract: * <ul> * <li>It must take a HTTP request as the first argument</li> * <li>It must take a HTTP response as the second argument</li> * <li> * It must take an URL as the third argument. This url is used to redirect * the user back to the originally used authorization flow, if the resolver * completes successfully. * </li> * <li> * It must take a callback as the fourth argument. This callback should be * invoked if and only if the resolver determines that it does not have to * start the organization resolving process. * </li> * </ul> * * @method * * @param {Object} config - Express stormpath configuration * @return {Function} Organization resolver function */ module.exports = function (config) { var configuredResolver = config.web.multiTenancy.organizationResolver; return (configuredResolver && typeof configuredResolver === 'function') ? configuredResolver : defaultOrganizationResolver; }; |