Example #1
0
var _deletePrincipalFields = function(principalId, profileFields, callback) {
    // Remove the specified fields
    var query = util.format('DELETE "%s" FROM "Principals" where "principalId" = ?', profileFields.join('", "'));
    Cassandra.runQuery(query, [principalId], function(err) {
        if (err) {
            return callback(err);
        }

        // If the principal is a user, invalidate their cache entry. They are being invalidated
        // rather than simply updated in the cache because we have removed fields
        OaeUtil.invokeIfNecessary(isUser(principalId), invalidateCachedUsers, [principalId], function(err) {
            if (err) {
                return callback(err);
            }

            // This is an update, so we also need to touch the principal
            _updatePrincipal(principalId, {}, function(err) {
                if (err) {
                    // We bypass indicating an error to the consumer in this case as we have
                    // successfully removed the fields in both Cassandra and the cache
                    log().warn({
                        'err': err,
                        'principalId': principalId
                    }, 'An unexpected error occurred while trying to touch a principal timestamp');
                }

                return callback();
            });
        });
    });
};
Example #2
0
var createMeeting = module.exports.createMeeting = function(createdBy, displayName, description, record, allModerators, waitModerator, visibility, opts, callback) {
    opts = opts || {};

    var created = opts.created || Date.now();
    created = created.toString();

    var tenantAlias = AuthzUtil.getPrincipalFromId(createdBy).tenantAlias;
    var meetingId = _createMeetingId(tenantAlias);
    var storageHash = {
        'tenantAlias': tenantAlias,
        'createdBy': createdBy,
        'displayName': displayName,
        'description': description,
        'record': record,
        'allModerators': allModerators,
        'waitModerator': waitModerator,
        'visibility': visibility,
        'created': created,
        'lastModified': created
    };

    var query = Cassandra.constructUpsertCQL('Meetings', 'id', meetingId, storageHash);
    Cassandra.runQuery(query.query, query.parameters, function(err) {
        if (err) {
            console.info(err);
            return callback(err);
        }

        return callback(null, _storageHashToMeeting(meetingId, storageHash));
    });
};
Example #3
0
var createDiscussion = module.exports.createDiscussion = function(createdBy, displayName, description, visibility, opts, callback) {
    opts = opts || {};

    var created = opts.created || Date.now();

    var tenantAlias = AuthzUtil.getPrincipalFromId(createdBy).tenantAlias;
    var discussionId = _createDiscussionId(tenantAlias);
    var storageHash = {
        'tenantAlias': tenantAlias,
        'createdBy': createdBy,
        'displayName': displayName,
        'description': description,
        'visibility': visibility,
        'created': created,
        'lastModified': created
    };

    var query = Cassandra.constructUpsertCQL('Discussions', 'id', discussionId, storageHash, 'QUORUM');
    Cassandra.runQuery(query.query, query.parameters, function(err) {
        if (err) {
            return callback(err);
        }

        return callback(null, _storageHashToDiscussion(discussionId, storageHash));
    });
};
Example #4
0
const getPreviewUris = function(revisionIds, callback) {
  revisionIds = _.uniq(revisionIds);
  if (_.isEmpty(revisionIds)) {
    return callback(null, {});
  }

  Cassandra.runQuery(
    'SELECT "revisionId", "thumbnailUri", "wideUri" FROM "Revisions" WHERE "revisionId" IN ?',
    [revisionIds],
    (err, rows) => {
      if (err) {
        return callback(err);
      }

      const previews = {};
      rows.forEach(row => {
        const revisionId = row.get('revisionId');
        previews[revisionId] = {};

        const thumbnailUri = row.get('thumbnailUri');
        if (thumbnailUri) {
          previews[revisionId].thumbnailUri = thumbnailUri;
        }

        const wideUri = row.get('wideUri');
        if (wideUri) {
          previews[revisionId].wideUri = wideUri;
        }
      });

      return callback(null, previews);
    }
  );
};
Example #5
0
var getContentPreviewsMetadata = module.exports.getContentPreviewsMetadata = function(contentIds, callback) {
    if (!contentIds || !contentIds.length) {
        return callback(null, {});
    }

    Cassandra.runQuery('SELECT lastModified, previews FROM Content WHERE contentId IN (?)', [contentIds], function(err, rows) {
        if (err) {
            return callback(err);
        }

        var previews = {};
        rows.forEach(function(row) {
            var lastModified = row.get('lastModified');
            var preview = row.get('previews');
            if (preview && preview.value && lastModified && lastModified.value) {
                lastModified = lastModified.value;
                try {
                    preview = JSON.parse(preview.value);
                } catch (err) {
                    log().warn({'contentId': row.key, 'previews': preview.value}, 'Could not parse preview data for content item.');
                    return;
                }

                previews[row.key] = {'lastModified': lastModified, 'previews': preview};
            }
        });

        return callback(null, previews);
    });
};
Example #6
0
var updatePrincipal = module.exports.updatePrincipal = function(principalId, profileFields, callback) {
    var validator = new Validator();

    // Ensure we're using a real principal id. If we weren't, we would be dangerously upserting an invalid principal
    validator.check(principalId, {'code': 400, 'msg': 'Attempted to update a principal with a non-principal id'}).isPrincipalId();

    // Ensure the caller is not trying to set an invalid field
    var invalidKeys = _.intersection(RESTRICTED_FIELDS, _.keys(profileFields));
    validator.check(invalidKeys.length, {'code': 400, 'msg': 'Attempted to update an invalid property'}).max(0);
    if (validator.hasErrors()) {
        return callback(validator.getFirstError());
    }

    var q = Cassandra.constructUpsertCQL('Principals', 'principalId', principalId, profileFields, 'QUORUM');
    if (!q) {
        return callback({'code': 500, 'msg': 'Unable to store profile fields'});
    }

    Cassandra.runQuery(q.query, q.parameters, function(err) {
        if (err) {
            return callback(err);
        }

        if (isUser(principalId)) {
            // Update the user in cache

            // Ensure the `principalId` is not part of the hash to avoid revalidating a stale cache entry
            profileFields = _.extend({}, profileFields);
            delete profileFields.principalId;
            return _updateCachedUser(principalId, profileFields, callback);
        } else {
            return callback();
        }
    });
};
Example #7
0
var getPreviewUris = module.exports.getPreviewUris = function(revisionIds, callback) {
    revisionIds = _.uniq(revisionIds);
    if (!revisionIds || revisionIds.length === 0) {
        return callback(null, {});
    }

    Cassandra.runQuery('SELECT thumbnailUri, wideUri FROM Revisions WHERE revisionId IN (?)', [revisionIds], function(err, rows) {
        if (err) {
            return callback(err);
        }

        var previews = {};
        rows.forEach(function(row) {
            previews[row.key] = {};

            var thumbnailUri = row.get('thumbnailUri');
            if (thumbnailUri && thumbnailUri.value) {
                previews[row.key].thumbnailUri = thumbnailUri.value;
            }

            var wideUri = row.get('wideUri');
            if (wideUri && wideUri.value) {
                previews[row.key].wideUri = wideUri.value;
            }
        });
        return callback(null, previews);
    });
};
Example #8
0
var updateContent = module.exports.updateContent = function(contentObj, profileUpdates, librariesUpdate, callback) {
    // Set the lastModified timestamp.
    var oldLastModified = contentObj.lastModified;
    profileUpdates.lastModified = Date.now();

    var q = Cassandra.constructUpsertCQL('Content', 'contentId', contentObj.id, profileUpdates, 'QUORUM');
    Cassandra.runQuery(q.query, q.parameters, function(err) {
        if (err) {
            return callback(err);
        }

        // Create the new content object by merging in the metadata changes over the old content object
        var newContentObj = _.extend({}, contentObj, profileUpdates);
        if (!librariesUpdate) {
            return callback(null, newContentObj);
        } else {
            _updateLibraries(newContentObj, oldLastModified, newContentObj.lastModified, [], function(err) {
                if (err) {
                    return callback(err);
                }
                callback(null, newContentObj);
            });
        }
    });
};
Example #9
0
    canCreateGroup(ctx, groupId, function(err) {
        if (err) {
            return callback(err);
        }

        // Create the group.
        Cassandra.runQuery('INSERT INTO Principals (principalId, alias, tenant, displayName, description, visibility, joinable) VALUES (?, ?, ?, ?, ?, ?, ?) USING CONSISTENCY QUORUM',
            [groupId, alias, tenant.alias, displayName, description, visibility, joinable], function (err) {
            if (err) {
                return callback(err);
            }

            // Immediately add the current user as a manager
            var currentUser = getUserId(ctx);
            members[currentUser] = Constants.roles.MANAGER;

            var opts = {
                'displayName': displayName,
                'description': description,
                'visibility': visibility,
                'joinable': joinable
            };
            var group = new Group(tenant.alias, groupId, alias, opts);
            _setGroupMembers(ctx, group, members, function(err) {
                PrincipalsEmitter.emit(PrincipalsConstants.events.CREATED_GROUP, ctx, group, members);
                if (err) {
                    return callback(err);
                }

                return callback(null, groupId);
            });
        });
    });
Example #10
0
var getActivitiesFromStreams = module.exports.getActivitiesFromStreams = function(activityStreamIds, start, callback) {
    var query = 'SELECT * FROM "ActivityStreams" WHERE "activityStreamId" IN ?';
    var parameters = [ activityStreamIds ];
    if (start) {
        query += ' AND "activityId" > ?';
        parameters.push(start + ':');
    }

    Cassandra.runQuery(query, parameters, function(err, rows) {
        if (err) {
            return callback(err);
        }

        var activitiesPerStream = {};
        _.each(rows, function(row) {
            var activityStreamId = row.get('activityStreamId');
            var activityId = row.get('activityId');
            var activityStr = row.get('activity');
            try {
                var activity = JSON.parse(activityStr);
                activitiesPerStream[activityStreamId] = activitiesPerStream[activityStreamId] || [];
                activitiesPerStream[activityStreamId].push(activity);
            } catch (err) {
                activityStr = activityStr.slice(0, 300);
                log().warn({'err': err, 'activityId': activityId, 'value': activityStr}, 'Error parsing activity from Cassandra');
            }
        });

        return callback(null, activitiesPerStream);
    });
};
Example #11
0
var _cacheTenants = function(callback) {
    callback = callback || function() {};

    // Get the available tenants
    Cassandra.runQuery('SELECT * FROM "Tenant"', false, function(err, rows) {
        if (err) {
            return callback(err);
        }

        // Reset the previously cached tenants
        tenants = {};
        tenantsByHost = {};

        // Create a dummy tenant object that can serve as the global admin tenant object
        globalTenant = new Tenant(serverConfig.globalAdminAlias, 'Global admin server', serverConfig.globalAdminHost, {'isGlobalAdminServer': true});

        // Cache it as part of the available tenants
        tenants[globalTenant.alias] = globalTenant;
        tenantsByHost[globalTenant.host] = globalTenant;

        _.each(rows, function(row) {
            var tenant = mapToTenant(row);
            // Cache all tenants
            tenants[tenant.alias] = tenant;
            tenantsByHost[tenant.host] = tenant;
        });

        return callback(null, tenants);
    });
};
Example #12
0
const _runQueryIfSpecified = function(query, callback) {
  if (!query) {
    return callback();
  }

  Cassandra.runQuery(query.query, query.parameters, callback);
};
Example #13
0
var acceptTermsAndConditions = module.exports.acceptTermsAndConditions = function(userId, callback) {
    Cassandra.runQuery('UPDATE "Principals" SET "acceptedTC" = ? WHERE "principalId" = ?', [Date.now().toString(), userId], function(err) {
        if (err) {
            return callback(err);
        }

        return invalidateCachedUsers([userId], callback);
    });
};
Example #14
0
var getPrincipals = module.exports.getPrincipals = function(principalIds, fields, callback) {
    if (_.isEmpty(principalIds)) {
        return callback(null, {});
    }

    // If we're only requesting 1 principal we can hand it off to the getPrincipal method.
    // This will try looking in the cache first, which might be faster
    if (principalIds.length === 1) {
        getPrincipal(principalIds[0], function(err, user) {
            if (err && err.code === 404) {
                // This method never returns an error if any principals in the listing are missing,
                // even if it is just a listing of 1 principal (e.g., a library of 1 item)
                return callback(null, {});
            } else if (err) {
                return callback(err);
            }

            var users = {};
            users[principalIds[0]] = user;
            return callback(null, users);
        });
        return;
    }

    // Build the query and parameters to select just the specified fields
    var query = null;
    var parameters = [];

    // If `fields` was specified, we select only the fields specified. Otherwise we select all (i.e., *)
    if (fields) {
        var columns = [];
        _.map(fields, function(field) {
            columns.push(util.format('"%s"', field));
        });
        query = 'SELECT ' + columns.join(',') + ' FROM "Principals" WHERE "principalId" IN (?)';
    } else {
        query = 'SELECT * FROM "Principals" WHERE "principalId" IN (?)';
    }

    parameters.push(principalIds);

    Cassandra.runQuery(query, parameters, function(err, rows) {
        if (err) {
            return callback(err);
        }

        var principals = _.chain(rows)
            .map(_getPrincipalFromRow)
            .compact()
            .indexBy('id')
            .value();

        return callback(null, principals);
    });
};
Example #15
0
var updateDiscussion = module.exports.updateDiscussion = function(discussion, profileFields, callback) {
    var storageHash = _.extend({}, profileFields);
    storageHash.lastModified = storageHash.lastModified || Date.now();

    var query = Cassandra.constructUpsertCQL('Discussions', 'id', discussion.id, storageHash, 'QUORUM');
    Cassandra.runQuery(query.query, query.parameters, function(err) {
        if (err) {
            return callback(err);
        }

        return callback(null, _createUpdatedDiscussionFromStorageHash(discussion, storageHash));
    });
};
Example #16
0
var getEmailToken = module.exports.getEmailToken = function(userId, callback) {
    Cassandra.runQuery('SELECT * FROM "PrincipalsEmailToken" WHERE "principalId" = ?', [userId], function(err, rows) {
        if (err) {
            return callback(err);
        } else if (_.isEmpty(rows)) {
            return callback({'code': 404, 'msg': 'No email token found for the given user id'});
        }

        var token = _.first(rows).get('token').value;
        var email = _.first(rows).get('email').value;
        return callback(null, email, token);
    });
};
Example #17
0
var isStale = module.exports.isStale = function(indexName, libraryId, visibility, callback) {
    // Select both the high and low slug column from the library
    var cql = 'SELECT "value" FROM "LibraryIndex" WHERE "bucketKey" = ? AND "rankedResourceId" IN (?)';
    Cassandra.runQuery(cql, [_createBucketKey(indexName, libraryId, visibility), [SLUG_HIGH, SLUG_LOW]], function(err, rows) {
        if (err) {
            return callback(err);
        }

        // If we got exactly 2 rows, it means that both the high and low slug were there, so the
        // library index is recent
        return callback(null, (rows.length !== 2));
    });
};
Example #18
0
var createTenant = module.exports.createTenant = function(ctx, alias, displayName, host, callback) {
    callback = callback || function() {};

    if (!ctx.user() || !ctx.user().isGlobalAdmin()) {
        return callback({'code': 401, 'msg': 'Only global administrators can create new tenants'});
    }

    var validator = new Validator();
    validator.check(alias, {'code': 400, 'msg': 'Missing alias'}).notEmpty();
    validator.check(alias, {'code': 400, 'msg': 'The tenant alias should not contain a space'}).notContains(' ');
    validator.check(alias, {'code': 400, 'msg': 'The tenant alias should not contain a colon'}).notContains(':');
    validator.check(displayName, {'code': 400, 'msg': 'Missing tenant displayName'}).notEmpty();
    validator.check(host, {'code': 400, 'msg': 'Missing tenant host'}).notEmpty();
    if (validator.hasErrors()) {
        return callback(validator.getFirstError());
    }

    // Make sure the tenant host name is all lower-case
    host = host.toLowerCase();

    // Make sure that a tenant with the same alias doesn't exist yet
    if (!getTenant(alias)) {
        // Make sure that a tenant with the same hostname doesn't exist yet
        if (!getTenantByHost(host)) {
            // Create the tenant
            var tenant = new Tenant(alias, displayName, host);
            Cassandra.runQuery('UPDATE "Tenant" SET "displayName" = ?, "host" = ?, "active" = ? WHERE "alias" = ?',
                [tenant.displayName, host, tenant.active, tenant.alias], function(err) {
                if (err) {
                    return callback(err);
                }

                // Send a message to all the app servers in the cluster notifying them that the tenant should be started
                Pubsub.publish('oae-tenants', 'start ' + tenant.alias, function(err) {
                    if (err) {
                        return callback(err);
                    }

                    // Let the configuration module know that a new tenant has been created and configuration needs to be fetched
                    Pubsub.publish('oae-config', tenant.alias);
                    callback(null, tenant);
                });
            });
        } else {
            callback({'code': 400, 'msg': 'A tenant with the host ' + host + ' already exists'});
        }
    } else {
        callback({'code': 400, 'msg': 'A tenant with the alias ' + alias + ' already exists'});
    }
};
Example #19
0
var getRevision = module.exports.getRevision = function(revisionId, callback) {
    Cassandra.runQuery('SELECT * FROM Revisions WHERE revisionId = ?', [revisionId], function (err, rows) {
        if (err) {
            return callback(err);
        }

        // Cassandra always returns the key as a column so the count property will always be 1.
        if (rows[0].count <= 1) {
            return callback({'code': 404, 'msg': 'Couldn\'t find revision: ' + revisionId});
        }

        var revision = _rowToRevision(rows[0]);
        callback(null, revision);
    });
};
Example #20
0
var getContent = module.exports.getContent = function(contentId, callback) {
    Cassandra.runQuery('SELECT * FROM Content USING CONSISTENCY QUORUM WHERE contentId = ?', [contentId], function (err, rows) {
        if (err) {
            return callback(err);
        }

        // Cassandra always returns the key as a column so the count will always be 1.
        if (rows[0].count <= 1) {
            return callback({'code': 404, 'msg': 'Couldn\'t find content: ' + contentId}, null);
        }

        var contentObj = _rowToContent(rows[0]);
        return callback(null, contentObj);
    });
};
Example #21
0
var updateMeeting = module.exports.updateMeeting = function(meeting, profileFields, callback) {
    var storageHash = _.extend({}, profileFields);
    storageHash.lastModified = storageHash.lastModified || Date.now();
    storageHash.lastModified = storageHash.lastModified.toString();

    var query = Cassandra.constructUpsertCQL('Meetings', 'id', meeting.id, storageHash);
    Cassandra.runQuery(query.query, query.parameters, function(err) {
        if (err) {
            console.info(err);
            return callback(err);
        }

        return callback(null, _createUpdatedMeetingFromStorageHash(meeting, storageHash));
    });
};
Example #22
0
var _getUserIdFromLoginId = function(loginId, callback) {
    Cassandra.runQuery('SELECT userId FROM AuthenticationLoginId USING CONSISTENCY QUORUM WHERE loginId = ?', [_flattenLoginId(loginId)], function(err, rows) {
        if (err) {
            return callback(err);
        }

        var row = rows[0];
        if (row) {
            var result = Cassandra.rowToHash(row);
            return callback(null, result.userId);
        } else {
            return callback();
        }
    });
};
Example #23
0
    Cassandra.runQuery(q.query, q.parameters, function(err) {
        if (err) {
            return callback(err);
        }

        // Add the revision to the list.
        Cassandra.runQuery('UPDATE RevisionByContent SET ?=? WHERE contentId=?', [values.created, revisionId, contentId], function(err) {
            if (err) {
                return callback(err);
            }

            var revision = new Revision(contentId, revisionId, values.createdBy, values.created, opts);
            callback(null, revision);
        });
    });
Example #24
0
var getPrincipals = module.exports.getPrincipals = function(principalIds, callback) {
    if (!principalIds || principalIds.length === 0) {
        return callback(null, {});
    }

    // If we're only requesting 1 principal we can hand it off to the getPrincipal method.
    // This will try looking in the cache first, which might be faster.
    if (principalIds.length === 1) {
        return getPrincipal(principalIds[0], function(err, user) {
            if (err) {
                return callback(err);
            }

            var users = {};
            users[principalIds[0]] = user;
            return callback(null, users);
        });
    }

    Cassandra.runQuery('SELECT * FROM Principals USING CONSISTENCY QUORUM WHERE principalId IN (?)', [principalIds], function(err, rows) {
        if (err) {
            return callback(err);
        }

        var principals = {};
        var missing = [];
        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            var principal = getPrincipalFromRow(row);
            if (principal) {
                principals[principal.id] = principal;
            } else {
                missing.push(row.get('principalId').value);
            }
        }

        if (missing.length > 0) {
            return callback({
                'code': 400,
                'msg': 'Some principals could not be found',
                'existing': _.keys(principals),
                'missing': missing
            });
        }

        return callback(null, principals);
    });
};
Example #25
0
var getUserIdsByEmails = module.exports.getUserIdsByEmails = function(emails, callback) {
    Cassandra.runQuery('SELECT * FROM "PrincipalsByEmail" WHERE "email" IN (?)', [emails], function(err, rows) {
        if (err) {
            return callback(err);
        }

        var userIdsByEmail = _.chain(rows)
            .map(Cassandra.rowToHash)
            .groupBy('email')
            .mapObject(function(principalIdEmailHashes) {
                return _.pluck(principalIdEmailHashes, 'principalId');
            })
            .value();
        return callback(null, userIdsByEmail);
    });
};
Example #26
0
var writeConfig = module.exports.writeConfig = function(tenantId, configValues, callback) {
    var validator = new Validator();
    validator.check(tenantId, {'code': 400, 'msg': 'Missing tenantid'}).notEmpty();
    validator.check(_.keys(configValues).length, {'code': 400, 'msg': 'Missing configuration. Example configuration: {"oae-authentication/google-authentication/google-authentication-enabled": {"tenantid": "global","value": false}}'}).min(1);
    if (validator.hasErrors()) {
        return callback(validator.getFirstError());
    }

    var q = Cassandra.constructUpsertCQL('Config', 'tenantId', tenantId, configValues, 'QUORUM');
    Cassandra.runQuery(q.query, q.parameters, function(err, config) {
        if (!err) {
            Pubsub.publish('oae-config', tenantId + ' config updated');
        }
        callback(err, config);
    });
};
Example #27
0
var setAdmin = module.exports.setAdmin = function(adminType, isAdmin, userId, callback) {
    // Ensure we're using a real principal id. If we weren't, we would be dangerously upserting an invalid row
    var validator = new Validator();
    validator.check(userId, {'code': 400, 'msg': 'Attempted to update a principal with a non-principal id'}).isPrincipalId();
    if (validator.hasErrors()) {
        return callback(validator.getError());
    }

    Cassandra.runQuery(util.format('UPDATE "Principals" SET "%s" = ? WHERE "principalId" = ?', adminType), [sanitize(isAdmin).toBooleanStrict().toString(), userId], function(err) {
        if (err) {
            return callback(err);
        }

        return invalidateCachedUsers([userId], callback);
    });
};
Example #28
0
var getConfigFromCassandra = function(tenantId, callback) {
    var cqlParams = [OAE.serverTenant.alias];
    if (tenantId !== OAE.serverTenant.alias) {
        cqlParams.push(tenantId);
    }

    Cassandra.runQuery('SELECT * FROM Config USING CONSISTENCY QUORUM WHERE tenantId IN (?)', [cqlParams], function(err, rows) {
        if (err) {
            return callback(err);
        }

        getDefaultConfiguration(function(defaultConfig) {
            callback(false, mergeStoredConfigIntoOriginal(rows, defaultConfig));
        });
    });
};
Example #29
0
var getAllTenants = module.exports.getAllTenants = function(callback) {
    Cassandra.runQuery('SELECT * FROM Tenant USING CONSISTENCY QUORUM', false, function(err, rows) {
        if (err) {
            return callback(err);
        }

        var tenants = {};
        for (var i = 0; i < rows.length; i++) {
            var tenant = mapToTenant(rows[i], false);
            if (!tenant.deleted) {
                tenants[tenant.alias] = tenant;
            }
        }
        callback(null, tenants);
    });
};
 err => {
   if (err) {
     _notifyOfError('AuthenticationUserLoginId', userHash, err, callback);
   }
   Cassandra.runQuery(
     'INSERT INTO "AuthenticationLoginId" ("loginId", "userId") VALUES (?, ?)',
     [newLoginId, userId],
     err => {
       if (err) {
         _notifyOfError('AuthenticationLoginId', userHash, err, callback);
       }
       log().info('Created Shibboleth login record for user %s', userHash.displayName);
       return callback();
     }
   );
 }