Example #1
0
 beforeCreate: function(instance){
     if(instance.providerId === 'saml'){
         return instance.sequelize.Promise.join(
             //generate a certificate
             pem.createCertificateAsync({
                 commonName: Optional.ofNullable(config.get('server.rootUrl'))
                                 .map(function(rootUrl){
                                     return url.format(rootUrl).hostname;
                                 })
                                 .orElse('localhost'),
                 days: 7300,
                 selfSigned: true,
                 keyBitsize: 2048
             })
             .then(function(generated){
                 return instance.sequelize.models.samlServiceProviderMetadata.create({
                     privateKey: generated.clientKey,
                     x509SigningCert: generated.certificate,
                     directoryId: instance.directoryId,
                     tenantId: instance.tenantId
                 })
                 .then(function(metadata){
                     instance.set('samlServiceProviderMetadataId', metadata.id);
                 });
             }),
             //create empty statement mapping rules
             instance.sequelize.models.attributeStatementMappingRules.create({tenantId: instance.tenantId})
                 .then(function(rules){
                     instance.set('attributeStatementMappingRulesId', rules.id);
                 })
         );
     }
 },
Example #2
0
function parseExpandParam(expandParam) {
    return Optional.ofNullable(expandParam)
        .filter(_.negate(_.isEmpty))
        //split the different parts, e.g: 'tenant,groups(offset:0,limit:10)'
        // ==> ['tenant', 'groups(offset:0,limit:10)']
        .map(_.method('split', /,(?!offset|limit)/))
        .map(function (expandStrings) {
            return _(expandStrings)
                .map(function (expandString) {
                    //separate the association name and the pagination parts
                    const expandParts = /^([^(]*)\(([^)]*)\)*$/.exec(expandString);
                    let associationName, pagination;
                    if (expandParts) {
                        //pagination (limit or offset) was specified
                        associationName = expandParts[1];
                        pagination = _(expandParts[2].split(','))
                            .map(_.method('split', ':'))
                            .fromPairs()
                            .mapValues(parseInt)
                            .defaults(defaultPagination)
                            .value();
                        ApiError.assert(_.keysIn(pagination).length === 2, ApiError, 400, 400, 'Invalid expansion pagination: %s', expandParts[2]);
                    } else {
                        //no option, the whole param is the association name
                        associationName = expandString;
                        pagination = defaultPagination;
                    }
                    return [associationName, pagination];
                })
                .fromPairs()
                .value();
        })
        .orElseGet(_.stubObject);
}
Example #3
0
 .then(function (apiKey) {
     done(
         null,
         Optional.ofNullable(apiKey)
             .filter(_.flow(_.property('secret'), _.partial(_.eq, providedSecret)))
             .orElse(false)
     );
     return null;
 })
Example #4
0
controller.getCurrent = function (req, res) {
    res.status(302)
        .location(
            Optional.ofNullable(config.get('server.rootUrl'))
                .map(_.method('concat', '/v1/accounts/', req.user.accountId))
                .orElse(req.user.accountId)
        )
        .json();
    return null;
};
Example #5
0
exports.send  = function(account, directory, template, tokenId, additionalPlaceHolderValues){
    var placeHolderValues = _.merge(
        //selection of account & directory fields
        {account: _.pick(_.defaults(account, {directory: _.pick(directory, directoryFields)}), accountFields)},
        //token related fields (url, name value pair...)
        Optional.ofNullable(tokenId).map(_.bindKey(template, 'getUrlTokens')).orElseGet(_.stubObject),
        //custom fields
        additionalPlaceHolderValues
    );

    var emailFields = _.defaults (
         {},
        //configuration fields
        config.get('email.fields'),
        //mandrill fields
        Optional.ofNullable(template.mandrillTemplate)
                .filter(() => _.eq(transportName, 'nodemailer-mandrill-transport'))
                .map(_.partial(getMandrillFields, _, placeHolderValues))
                .orElseGet(_.stubObject),
        //template fields
        {
            from: template.fromName + '<'+template.fromEmailAddress+'>',
            to: account.email,
            subject: template.subject,
            text: _.template(template.textBody, templateSettings)(placeHolderValues)
        },
        //Add html alternative only if the mime type is text/html
        Optional.of(template)
            .filter(_.flow(_.property('mimeType'), _.partial(_.eq, 'text/html')))
            .map(function(t){
               return {html: _.template(t.htmlBody, templateSettings)(placeHolderValues)};
            })
            .orElseGet(_.stubObject)
    );

    return transporterSendEmail(emailFields)
            .tap(_.partial(logger.info, 'Email sent:'))
            .catch(_.partial(logger.error, 'Could not send email:'));
};
Example #6
0
 getLookupAccountStore: function (organizationName) {
     //if organizationName is specified, try to find an organization
     //mapped to this application with that name.
     //Else return this application
     return Optional.ofNullable(organizationName)
         .map(o => this.getOrganizations({
             attributes: ['id'],
             where: {name: o},
             limit: 1
         })
             .then(_.head)
             .tap(_.partial(ApiError.assert, _, ApiError, 404, 2014, 'Organization %s is not linked to application %s', organizationName, this.id)))
         .orElse(this.sequelize.Promise.resolve(this));
 }
Example #7
0
controller.createFactor = function (req, res) {
    const accountId = req.swagger.params.id.value;
    const factorAttributes = req.swagger.params.attributes.value;
    //if accountName is not set, take the account email
    BluebirdPromise.resolve(
        Optional.ofNullable(factorAttributes.accountName)
            .orElseGet(() => models.account.findByPk(accountId, {attributes: ['email']})
                .tap(ApiError.assertFound)
                .get('email'))
    ).then(email => {
        factorAttributes.accountName = email;
        return controllerHelper.createAndExpand(
            models.factor,
            {tenantId: req.user.tenantId, accountId: accountId},
            req, res);
    });
};
Example #8
0
 .then(requireMfa => {
     var secondFactor = Optional.ofNullable(factorTypeGetter)
                         .map(_.method('call', null, result))
                         .orElse(null);
     //ask for a second factor if requested
     if(!_.isEmpty(requireMfa) && !_.includes(requireMfa, secondFactor)){
         req.authInfo.require_mfa = requireMfa;
         //add 2nd factors into the request scope
         req.authInfo.scope = scopeHelper.pathsToScope(_.merge(
             scopeHelper.scopeToPaths(req.authInfo.scope),
             idSiteHelper.getFactorsScope(accountId)
         ));
         req.authInfo.isNewSub = isNewSub;
         return setAuthorizationBearer(req, this)
                 .then(() => original.call(this, result));
     } else {
         //the user is authenticated: redirect back to the application
         return getApiKey(
             req.authInfo.sub, {
               model: models.tenant,
               include: [{
                 model: models.idSite,
                 limit: 1
               }]
             }
           )
           .then(function(apiKey) {
             return idSiteHelper.getJwtResponse(
                 apiKey,
                 accountHref,
                 {
                     isNewSub: isNewSub || req.authInfo.isNewSub || false,
                     status: "AUTHENTICATED",
                     cb_uri: req.authInfo.cb_uri,
                     irt: req.authInfo.init_jti,
                     state: req.authInfo.state,
                     mfa: secondFactor,
                     inv_href: req.authInfo.inv_href,
                     email: req.authInfo.email
                 }
             );
           })
           //don't redirect directly to the app, redirect first to cloudpass so it can set a cookie
           .then(jwtResponse => this.redirect(hrefHelper.getRootUrl(req.authInfo.app_href) + "/sso?jwtResponse=" + jwtResponse));
     }
 })
 .orElseGet(() =>
     //look for orgId in authInfo (account settings or missing 2nd factor)
     Optional.ofNullable(req.authInfo.org_href)
         .orElseGet(() => {
             //ros => request organization selection
             if (req.authInfo.ros) {
                 return models.account.build({id: accountId}).countOrganizations()
                     .then(_.cond([
                         //no org => no need for selection
                         [_.partial(_.eq, 0), _.constant(null)],
                         //only one org => just take this one
                         [_.partial(_.eq, 1), () => models.account.build({id: accountId}).getOrganizations({
                             limit: 1,
                             attributes: ['id']
                         }).get(0).get('href')],
                         //more than one: selection needed
                         [_.stubTrue, _.stubFalse]
                     ]));
             } else {
                 //no org requested
                 return null;
             }
         })
Example #10
0
var _ = require('lodash');
var Optional = require('optional-js');
var winston = require('winston');
var scopeHelper = require('./scopeHelper');
var hrefHelper = require('./hrefHelper');

var logger = winston.loggers.get('email');
var templateSettings = {interpolate : /\${([\w\.]+?)}/g};
var accountFields = ['givenName', 'surname', 'fullName', 'username', 'email', 'failedLoginAttempts', 'directory'];
var directoryFields = ['name'];

// create reusable transporter object
var transportName = config.get('email.transport.name');
var transportOptions = config.get('email.transport.options');
var transporter = nodemailer.createTransport(
    Optional.ofNullable(transportName)
        .map(function(pluginName){return require(pluginName)(transportOptions);})
        .orElse(transportOptions)
);
var transporterSendEmail = BluebirdPromise.promisify(transporter.sendMail, {context: transporter});

function getMandrillFields(mandrillTemplate, placeHolderValues){
    return {
        //subject, content & from fields are provided by the template
        subject: null,
        text: null,
        html: null,
        from: null,
        mandrillOptions: {
            template_name: mandrillTemplate,
            template_content: [],
Example #11
0
 .flatMap(hrefSplit =>
     Optional.ofNullable(_.find(_.values(models.sequelize.models), _.matchesProperty('options.name.plural', hrefSplit[0])))
         .map(_.method('build', {id: hrefSplit[1]}))
Example #12
0
 return function(result) {
     //check if an account has been returned
     let accountHref = accountHrefGetter(result, req);
     if (accountHref) {
         var accountId = /\/accounts\/(.*)$/.exec(accountHref)[1];
         //request a 2nd factor if the user is not already authenticated
         // and the user already configured any or the application requested it
         BluebirdPromise.resolve(
                 Optional.ofNullable(req.authInfo.authenticated ? [] : req.authInfo.require_mfa)
                     .orElseGet(() =>
                         models.factor.findAll({
                             where: {
                                 accountId,
                                 status: 'ENABLED',
                                 verificationStatus: 'VERIFIED'
                             }
                         })
                         .map(_.property('type'))
                         .then(_.uniq)
                     )
         )
         .then(requireMfa => {
             var secondFactor = Optional.ofNullable(factorTypeGetter)
                                 .map(_.method('call', null, result))
                                 .orElse(null);
             //ask for a second factor if requested
             if(!_.isEmpty(requireMfa) && !_.includes(requireMfa, secondFactor)){
                 req.authInfo.require_mfa = requireMfa;
                 //add 2nd factors into the request scope
                 req.authInfo.scope = scopeHelper.pathsToScope(_.merge(
                     scopeHelper.scopeToPaths(req.authInfo.scope),
                     idSiteHelper.getFactorsScope(accountId)
                 ));
                 req.authInfo.isNewSub = isNewSub;
                 return setAuthorizationBearer(req, this)
                         .then(() => original.call(this, result));
             } else {
                 //the user is authenticated: redirect back to the application
                 return getApiKey(
                     req.authInfo.sub, {
                       model: models.tenant,
                       include: [{
                         model: models.idSite,
                         limit: 1
                       }]
                     }
                   )
                   .then(function(apiKey) {
                     return idSiteHelper.getJwtResponse(
                         apiKey,
                         accountHref,
                         {
                             isNewSub: isNewSub || req.authInfo.isNewSub || false,
                             status: "AUTHENTICATED",
                             cb_uri: req.authInfo.cb_uri,
                             irt: req.authInfo.init_jti,
                             state: req.authInfo.state,
                             mfa: secondFactor,
                             inv_href: req.authInfo.inv_href,
                             email: req.authInfo.email
                         }
                     );
                   })
                   //don't redirect directly to the app, redirect first to cloudpass so it can set a cookie
                   .then(jwtResponse => this.redirect(hrefHelper.getRootUrl(req.authInfo.app_href) + "/sso?jwtResponse=" + jwtResponse));
             }
         })
         .catch(req.next);
     } else {
         return original.call(this, result);
     }
 };
Example #13
0
 .then(requireMfa => {
     const secondFactor = Optional.ofNullable(factorTypeGetter)
         .map(_.method('call', null, result))
         .orElse(req.authInfo.verified_mfa);
     //ask for a second factor if requested
     if (!_.isEmpty(requireMfa) && !_.includes(requireMfa, secondFactor)) {
         req.authInfo.require_mfa = requireMfa;
         //add 2nd factors into the request scope
         req.authInfo.scope = scopeHelper.pathsToScope(_.merge(
             scopeHelper.scopeToPaths(req.authInfo.scope),
             idSiteHelper.getFactorsScope(accountId)
         ));
         req.authInfo.isNewSub = isNewSub;
         return setAuthorizationBearer(req, this)
             .then(() => original.call(this, result));
     } else {
         //the user is authenticated
         // check if organization selection was requested
         return BluebirdPromise.resolve(
             Optional.ofNullable(orgHrefGetter)
                 .map(_.method('call', null, result))
                 .orElseGet(() =>
                     //look for orgId in authInfo (account settings or missing 2nd factor)
                     Optional.ofNullable(req.authInfo.org_href)
                         .orElseGet(() => {
                             //ros => request organization selection
                             if (req.authInfo.ros) {
                                 return models.account.build({id: accountId}).countOrganizations()
                                     .then(_.cond([
                                         //no org => no need for selection
                                         [_.partial(_.eq, 0), _.constant(null)],
                                         //only one org => just take this one
                                         [_.partial(_.eq, 1), () => models.account.build({id: accountId}).getOrganizations({
                                             limit: 1,
                                             attributes: ['id']
                                         }).get(0).get('href')],
                                         //more than one: selection needed
                                         [_.stubTrue, _.stubFalse]
                                     ]));
                             } else {
                                 //no org requested
                                 return null;
                             }
                         })
                 ))
             .then(orgHref => {
                 if (orgHref === false) {
                     //organization requested but not chosen yet
                     //add available organizations to the scope
                     req.authInfo.scope = idSiteHelper.getAccountOrganizationsScope(accountId);
                     req.authInfo.verified_mfa = secondFactor;
                     req.authInfo.isNewSub = isNewSub;
                     return setAuthorizationBearer(req, this).then(() => original.call(this, result));
                 } else {
                     return getApiKey(
                         req.authInfo.sub, {
                             model: models.tenant,
                             include: [{
                                 model: models.idSite,
                                 limit: 1
                             }]
                         }
                     )
                         .then(function (apiKey) {
                             return idSiteHelper.getJwtResponse(
                                 apiKey,
                                 accountHref,
                                 {
                                     isNewSub: isNewSub || req.authInfo.isNewSub || false,
                                     status: "AUTHENTICATED",
                                     cb_uri: req.authInfo.cb_uri,
                                     irt: req.authInfo.init_jti,
                                     state: req.authInfo.state,
                                     mfa: secondFactor,
                                     inv_href: req.authInfo.inv_href,
                                     email: req.authInfo.email,
                                     org_href: orgHref
                                 }
                             );
                         })
                         //don't redirect directly to the app, redirect first to cloudpass so it can set a cookie
                         .then(jwtResponse => this.redirect(hrefHelper.getRootUrl(req.authInfo.app_href) + "/sso?jwtResponse=" + jwtResponse));
                 }
             });
     }
 })
Example #14
0
 v => Optional.ofNullable(v).map(_.property('href')).map(hrefHelper.resolveHref).orElse(v)
Example #15
0
 getFacebookAppSecret() {
     return Optional.ofNullable(process.env.FACEBOOK_CLIENT_SECRET)
         .orElseThrow(() => new Error('Could not get env variable FACEBOOK_CLIENT_SECRET'));
 }
Example #16
0
 afterAuthentication(result => Optional.ofNullable(result.href).filter(_.constant(result.status === 'ENABLED')).orElseGet(_.stubFalse), true)
Example #17
0
 verifyPassword: function(password){
     return Optional.ofNullable(this.get('password', {role: 'passwordHashing'}))
             .map(hash => bcrypt.compareAsync(password, hash))
             .orElse(BluebirdPromise.resolve(false));
 },
Example #18
0
const getHrefGroup = (groupId, defaultResult) =>
                        href =>  Optional.of(href)
                          .map(hrefPattern.exec.bind(hrefPattern))
                          .map(res => res[groupId])
                          .orElse(defaultResult);
//the root URL is only protocol + host
exports.getRootUrl = getHrefGroup(2, '');

//the base URL also includes the API version (/v1)
exports.getBaseUrl = getHrefGroup(1, '/v1');

//unqualifyHref removes the base URL
exports.unqualifyHref = href => href.replace(hrefPattern, '$3');

//returned the configured base URL
exports.baseUrl = Optional.ofNullable(config.get('server.rootUrl'))
                            .map(_.method('concat', '/v1/'))
                            .orElse('/');

exports.resolveHref = href => Optional.of(href)
        .map(exports.unqualifyHref)
        .map(_.method('split', '/'))
        .map(_.compact)
        .filter(_.matchesProperty('length', 2))
        .flatMap(hrefSplit =>
            Optional.ofNullable(_.find(_.values(models.sequelize.models), _.matchesProperty('options.name.plural', hrefSplit[0])))
                .map(_.method('build', {id: hrefSplit[1]}))
        ).orElse(null);

Example #19
0
 req => Optional.ofNullable(req.headers.authorization)
     .map(_.bindKey(authorization, 'parse'))
     .filter(auth => auth.scheme === 'Bearer')
     .map(_.property('token'))
     .orElse(null),