smc-hub
Version:
CoCalc: Backend webserver component
1,315 lines (1,296 loc) • 35.2 kB
JavaScript
// Generated by CoffeeScript 2.5.1
(function() {
//########################################################################
// This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
// License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
//########################################################################
/*
TESTING of User (and project) client queries
COPYRIGHT : (c) 2017 SageMath, Inc.
LICENSE : AGPLv3
*/
var SCHEMA, async, create_accounts, create_projects, db, expect, misc, pgtest, setup, teardown;
pgtest = require('./pgtest');
db = void 0;
setup = function(cb) {
return pgtest.setup(function(err) {
db = pgtest.db;
return cb(err);
});
};
teardown = pgtest.teardown;
({create_accounts, create_projects} = pgtest);
async = require('async');
expect = require('expect');
misc = require('smc-util/misc');
({SCHEMA} = require('smc-util/schema'));
describe('some basic testing of user_queries', function() {
var account_id, account_id2, project_id;
this.timeout(10000);
before(setup);
after(teardown);
account_id = void 0;
// First create an account, so we can do some queries.
it('creates an account', function(done) {
return db.create_account({
first_name: "Sage",
last_name: "Math",
created_by: "1.2.3.4",
email_address: "sage@example.com",
password_hash: "blah",
cb: function(err, x) {
account_id = x;
return done(err);
}
});
});
it('queries for the first_name and account_id property', function(done) {
return db.user_query({
account_id: account_id,
query: {
accounts: {
account_id: account_id,
first_name: null
}
},
cb: function(err, result) {
expect(result).toEqual({
accounts: {
account_id: account_id,
first_name: 'Sage'
}
});
return done(err);
}
});
});
it('query for the evaluate key fills in the correct default', function(done) {
return db.user_query({
account_id: account_id,
query: {
accounts: {
account_id: account_id,
evaluate_key: null
}
},
cb: function(err, result) {
var x;
x = SCHEMA.accounts.user_query.get.fields.evaluate_key;
expect(result).toEqual({
accounts: {
account_id: account_id,
evaluate_key: x
}
});
return done(err);
}
});
});
it('queries the collaborators virtual table before there are any projects', function(done) {
return db.user_query({
account_id: account_id,
query: {
collaborators: [
{
account_id: null,
first_name: null,
last_name: null
}
]
},
cb: function(err, collabs) {
if (err) {
done(err);
return;
}
expect(collabs).toEqual({
collaborators: []
});
return done();
}
});
});
project_id = void 0;
it('creates a project that we will query about soon', function(done) {
return db.create_project({
account_id: account_id,
title: "Test project",
description: "The description",
cb: function(err, x) {
project_id = x;
return done(err);
}
});
});
it('queries the collaborators virtual table after making one project', function(done) {
return db.user_query({
account_id: account_id,
query: {
collaborators: [
{
account_id: null,
first_name: null,
last_name: null
}
]
},
cb: function(err, collabs) {
var user;
if (err) {
done(err);
return;
}
user = {
account_id: account_id,
first_name: 'Sage',
last_name: 'Math'
};
expect(collabs).toEqual({
collaborators: [user]
});
return done();
}
});
});
it('queries the projects table and ensures there is one project with the correct title and description.', function(done) {
return db.user_query({
account_id: account_id,
query: {
projects: [
{
project_id: project_id,
title: null,
description: null
}
]
},
cb: function(err, projects) {
expect(projects).toEqual({
projects: [
{
description: 'The description',
project_id: project_id,
title: 'Test project'
}
]
});
return done(err);
}
});
});
it('changes the title of the project', function(done) {
return db.user_query({
account_id: account_id,
query: {
projects: {
project_id: project_id,
title: 'The new title',
description: 'The new description'
}
},
cb: done
});
});
it('and checks that the title/desc did indeed change', function(done) {
return db.user_query({
account_id: account_id,
query: {
projects: [
{
project_id: project_id,
title: null,
description: null
}
]
},
cb: function(err, projects) {
expect(projects).toEqual({
projects: [
{
description: 'The new description',
project_id: project_id,
title: 'The new title'
}
]
});
return done(err);
}
});
});
account_id2 = void 0;
it('create a second account...', function(done) {
return db.create_account({
first_name: "Elliptic",
last_name: "Curve",
created_by: "3.1.3.4",
email_address: "other@example.com",
password_hash: "blahblah",
cb: function(err, x) {
account_id2 = x;
return done(err);
}
});
});
it('queries with second account for the first_name and account_id property of first account', function(done) {
return db.user_query({
account_id: account_id2,
query: {
accounts: {
account_id: account_id,
first_name: null
}
},
cb: function(err, result) {
// we get undefined, meaning no results in the data we know about that match the query
expect(result).toEqual({
accounts: void 0
});
return done(err);
}
});
});
it('queries for first user project but does not see it', function(done) {
return db.user_query({
account_id: account_id2,
query: {
projects: [
{
project_id: project_id,
title: null,
description: null
}
]
},
cb: function(err, projects) {
expect(err).toEqual('FATAL: you do not have read access to this project');
return done();
}
});
});
it('queries the collaborators virtual table before there are any projects for the second user', function(done) {
return db.user_query({
account_id: account_id2,
query: {
collaborators: [
{
account_id: null,
first_name: null,
last_name: null
}
]
},
cb: function(err, collabs) {
if (err) {
done(err);
return;
}
expect(collabs).toEqual({
collaborators: []
});
return done();
}
});
});
it('add second user as a collaborator', function(done) {
return db.add_user_to_project({
project_id: project_id,
account_id: account_id2,
group: 'collaborator',
cb: done
});
});
it('queries again and finds that the second user can see the first project', function(done) {
return db.user_query({
account_id: account_id2,
query: {
projects: [
{
project_id: project_id,
title: null,
description: null,
users: null
}
]
},
cb: function(err, projects) {
var users;
users = {
[`${account_id}`]: {
group: 'owner'
},
[`${account_id2}`]: {
group: 'collaborator'
}
};
expect(projects).toEqual({
projects: [
{
description: 'The new description',
project_id: project_id,
title: 'The new title',
users: users
}
]
});
return done(err);
}
});
});
return it('queries the collaborators virtual table for the first user', function(done) {
return db.user_query({
account_id: account_id,
query: {
collaborators: [
{
account_id: null,
first_name: null,
last_name: null
}
]
},
cb: function(err, collabs) {
var user1, user2;
if (err) {
done(err);
return;
}
collabs.collaborators.sort(function(a, b) {
return misc.cmp(a.last_name, b.last_name); // make canonical
});
user1 = {
account_id: account_id2,
first_name: 'Elliptic',
last_name: 'Curve'
};
user2 = {
account_id: account_id,
first_name: 'Sage',
last_name: 'Math'
};
expect(collabs).toEqual({
collaborators: [user1, user2]
});
return done(err);
}
});
});
});
describe('testing file_use -- ', function() {
var accounts, projects, time0;
before(setup);
after(teardown);
// Create two users and two projects
accounts = [];
projects = [];
it('setup accounts and projects', function(done) {
return async.series([
function(cb) {
return create_accounts(2,
function(err,
x) {
accounts = x;
return cb();
});
},
function(cb) {
return create_projects(1,
accounts[0],
function(err,
x) {
projects.push(...x);
return cb(err);
});
},
function(cb) {
return create_projects(1,
accounts[1],
function(err,
x) {
projects.push(...x);
return cb(err);
});
}
], done);
});
time0 = new Date();
it('writes a file_use entry via a user query (and gets it back)', function(done) {
var obj;
obj = {
project_id: projects[0],
path: 'foo',
users: {
[`${accounts[0]}`]: {
edit: time0
}
},
last_edited: time0
};
return async.series([
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: obj
},
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: {
project_id: projects[0],
path: 'foo',
users: null,
last_edited: null
}
},
cb: function(err,
result) {
expect(result).toEqual({
file_use: obj
});
return cb(err);
}
});
}
], done);
});
it('writes another file_use entry and verifies that json is properly *merged*', function(done) {
var obj;
obj = {
project_id: projects[0],
path: 'foo',
users: {
[`${accounts[0]}`]: {
read: time0
}
}
};
return async.series([
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: obj
},
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: {
project_id: projects[0],
path: 'foo',
users: null,
last_edited: null
}
},
cb: function(err,
result) {
// add rest of what we expect from previous insert in test above:
obj.last_edited = time0;
obj.users[`${accounts[0]}`] = {
read: time0,
edit: time0
};
expect(result).toEqual({
file_use: obj
});
return cb(err);
}
});
}
], done);
});
it('tries to read file use entry as user without project access and fails', function(done) {
return db.user_query({
account_id: accounts[1],
query: {
file_use: {
project_id: projects[0],
path: 'foo',
users: null
}
},
cb: function(err, result) {
expect(err).toEqual('FATAL: you do not have read access to this project');
return done();
}
});
});
it('adds second user to first project, then reads and finds one file_use match', function(done) {
return async.series([
function(cb) {
return db.add_user_to_project({
project_id: projects[0],
account_id: accounts[1],
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: [
{
project_id: projects[0],
path: 'foo',
users: null
}
]
},
cb: function(err,
x) {
var ref;
expect(x != null ? (ref = x.file_use) != null ? ref.length : void 0 : void 0).toEqual(1);
return cb(err);
}
});
}
], done);
});
it('add a second file_use notification for first project (different path)', function(done) {
var obj, t;
t = new Date();
obj = {
project_id: projects[0],
path: 'foo2',
users: {
[`${accounts[1]}`]: {
read: t
}
},
last_edited: t
};
return async.series([
function(cb) {
return db.user_query({
account_id: accounts[1],
query: {
file_use: obj
},
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: [
{
project_id: projects[0],
path: 'foo',
users: null
}
]
},
cb: function(err,
x) {
var ref;
expect(x != null ? (ref = x.file_use) != null ? ref.length : void 0 : void 0).toEqual(1);
return cb(err);
}
});
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: [
{
project_id: projects[0],
path: null,
users: null
}
]
},
cb: function(err,
x) {
if (err) {
cb(err);
return;
}
expect(x.file_use.length).toEqual(2);
expect(x.file_use[0].path).toEqual('foo2'); // order will be this way due to sort of last_edited
expect(x.file_use[1].path).toEqual('foo');
return cb();
}
});
}
], done);
});
it('add a file_use notification for second project as second user', function(done) {
var obj;
obj = {
project_id: projects[1],
path: 'bar',
last_edited: new Date()
};
return db.user_query({
account_id: accounts[1],
query: {
file_use: obj
},
cb: done
});
});
it('confirm total of 3 file_use entries', function(done) {
return db.user_query({
account_id: accounts[1],
query: {
file_use: [
{
project_id: null,
path: null,
last_edited: null
}
]
},
cb: function(err, x) {
if (err) {
return done(err);
} else {
expect(x.file_use.length).toEqual(3);
return done();
}
}
});
});
it('also check limit option works', function(done) {
return db.user_query({
account_id: accounts[1],
query: {
file_use: [
{
project_id: null,
path: null
}
]
},
options: [
{
limit: 2
}
],
cb: function(err, x) {
if (err) {
return done(err);
} else {
expect(x.file_use.length).toEqual(2);
return done();
}
}
});
});
return it('verify that account 0 cannot write file_use notification to project 1; but as admin can.', function(done) {
var obj;
obj = {
project_id: projects[1],
path: 'bar',
last_edited: new Date()
};
return async.series([
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
file_use: obj
},
cb: function(err) {
expect(err).toEqual('FATAL: user must be an admin');
return cb();
}
});
},
function(cb) {
// now make account 0 an admin
return db.make_user_admin({
account_id: accounts[0],
cb: cb
});
},
function(cb) {
// verify user 0 is admin
return db.is_admin({
account_id: accounts[0],
cb: function(err,
is_admin) {
expect(is_admin).toEqual(true);
return cb(err);
}
});
},
function(cb) {
// ... but 1 is not
return db.is_admin({
account_id: accounts[1],
cb: function(err,
is_admin) {
expect(is_admin).toEqual(false);
return cb(err);
}
});
},
function(cb) {
// ... , and see that it can write to project not on
return db.user_query({
account_id: accounts[0],
query: {
file_use: obj
},
cb: cb
});
}
], done);
});
});
describe('test project_log table', function() {
var accounts, projects;
before(setup);
after(teardown);
// Create two users and one project
accounts = [];
projects = [];
it('setup accounts and projects', function(done) {
return async.series([
function(cb) {
return create_accounts(3,
function(err,
x) {
accounts = x;
return cb();
});
},
function(cb) {
return create_projects(3,
accounts[0],
function(err,
x) {
projects.push(...x);
return cb(err);
});
}
], done);
});
it('writes a project_log entry via a user query (and gets it back)', function(done) {
var obj;
obj = {
id: misc.uuid(),
project_id: projects[0],
time: new Date(),
event: {
test: 'thing'
}
};
return async.series([
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
project_log: obj
},
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
project_log: {
project_id: projects[0],
time: null,
event: null,
id: null
}
},
cb: function(err,
result) {
expect(result).toEqual({
project_log: obj
});
return cb(err);
}
});
}
], done);
});
it('write two project_log entries with the same timestamp (but different ids)', function(done) {
var obj0, obj1, t;
t = new Date();
obj0 = {
id: misc.uuid(),
project_id: projects[0],
time: t,
event: {
test: 'stuff',
a: ['x', 'y']
}
};
obj1 = {
id: misc.uuid(),
project_id: projects[0],
time: t, // SAME TIME
event: {
test: 'other stuff'
}
};
return async.series([
function(cb) {
return db.user_query({
account_id: accounts[0],
query: [
{
project_log: obj0
},
{
project_log: obj1
}
],
cb: cb
});
},
function(cb) {
// get everything with the given time t
return db.user_query({
account_id: accounts[0],
query: {
project_log: [
{
project_id: projects[0],
time: t,
id: null
}
]
},
cb: function(err,
result) {
if (err) {
return cb(err);
} else {
expect(result.project_log.length).toEqual(2);
expect(result.project_log[0].time).toEqual(t);
expect(result.project_log[1].time).toEqual(t);
return cb();
}
}
});
}
], done);
});
it("confirm other user can't read log of first project", function(done) {
return db.user_query({
account_id: accounts[1],
query: {
project_log: [
{
project_id: projects[0],
time: null,
id: null
}
]
},
cb: function(err, result) {
expect(err).toEqual('FATAL: you do not have read access to this project');
return done();
}
});
});
it('make third user an admin and verify can read log of first project', function(done) {
return async.series([
function(cb) {
// now make account 2 an admin
return db.make_user_admin({
account_id: accounts[2],
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[2],
query: {
project_log: [
{
project_id: projects[0],
time: null,
id: null
}
]
},
cb: function(err,
result) {
if (err) {
return cb(err);
} else {
expect(result.project_log.length).toEqual(3);
return cb();
}
}
});
}
], done);
});
it("add other user, and confirm other user now *CAN* read log", function(done) {
return async.series([
function(cb) {
return db.add_user_to_project({
project_id: projects[0],
account_id: accounts[1],
cb: cb
});
},
function(cb) {
return db.user_query({
account_id: accounts[1],
query: {
project_log: [
{
project_id: projects[0],
time: null,
id: null
}
]
},
cb: function(err,
result) {
expect(result.project_log.length).toEqual(3);
return cb(err);
}
});
}
], done);
});
it("confirm other project doesn't have any log entries (testing that reads are by project)", function(done) {
return db.user_query({
account_id: accounts[0],
query: {
project_log: [
{
project_id: projects[1],
time: null,
id: null
}
]
},
cb: function(err, result) {
expect(result.project_log.length).toEqual(0);
return done(err);
}
});
});
return it("add three entries to second project log and verify that they come back in the right order", function(done) {
var _, f, ids;
f = function(t, id, cb) {
var obj;
obj = {
id: id,
project_id: projects[2],
time: t,
event: {
test: '0'
}
};
return db.user_query({
account_id: accounts[0],
query: {
project_log: obj
},
cb: cb
});
};
ids = (function() {
var i, len, ref, results;
ref = [0, 1, 2];
results = [];
for (i = 0, len = ref.length; i < len; i++) {
_ = ref[i];
results.push(misc.uuid());
}
return results;
})();
return async.series([
function(cb) {
return f(misc.minutes_ago(5),
ids[0],
cb);
},
function(cb) {
return f(misc.minutes_ago(1),
ids[2],
cb);
},
function(cb) {
return f(misc.minutes_ago(3),
ids[1],
cb);
},
function(cb) {
return db.user_query({
account_id: accounts[0],
query: {
project_log: [
{
project_id: projects[2],
time: null,
id: null
}
]
},
cb: function(err,
result) {
if (err) {
return cb(err);
} else {
expect(result.project_log.length).toEqual(3);
expect(result.project_log[0].id).toEqual(ids[2]);
expect(result.project_log[1].id).toEqual(ids[1]);
expect(result.project_log[2].id).toEqual(ids[0]);
return cb();
}
}
});
}
], done);
});
});
describe('nonexistent tables', function() {
var account_id;
before(setup);
after(teardown);
account_id = void 0;
it('creates account', function(done) {
return create_accounts(1, function(err, accounts) {
account_id = accounts != null ? accounts[0] : void 0;
return done(err);
});
});
it('write to non-existent table', function(done) {
return db.user_query({
account_id: account_id,
query: {
nonexistent_table: {
foo: 'bar'
}
},
cb: function(err) {
expect(err).toEqual("FATAL: table 'nonexistent_table' does not exist");
return done(!err);
}
});
});
it('read from non-existent table (single thing)', function(done) {
return db.user_query({
account_id: account_id,
query: {
nonexistent_table: {
foo: null
}
},
cb: function(err) {
expect(err).toEqual("FATAL: get queries not allowed for table 'nonexistent_table'");
return done(!err);
}
});
});
return it('read from non-existent table (multiple)', function(done) {
return db.user_query({
account_id: account_id,
query: {
nonexistent_table: [
{
foo: null
}
]
},
cb: function(err) {
expect(err).toEqual("FATAL: get queries not allowed for table 'nonexistent_table'");
return done(!err);
}
});
});
});
describe('test the get_account server query', function() {
var accounts, hash;
before(setup);
after(teardown);
accounts = void 0;
it('create two accounts', function(done) {
return create_accounts(2, function(err, x) {
accounts = x;
return done(err);
});
});
it('calls get_account with some columns for first account', function(done) {
return db.get_account({
account_id: accounts[0],
columns: ['account_id', 'email_address', 'password_is_set'],
cb: function(err, x) {
expect(x).toEqual({
account_id: accounts[0],
email_address: "sage+0@sagemath.com",
password_is_set: false
});
return done(err);
}
});
});
hash = 'sha512$4477684995985fb6bd2c9020d3f35c69$1000$41cc46a70ba52ade010b56ccbdca942af9271b256763479eb2d8d8283d1023e43745f4cc6fe7a970ce1cf28df6c9edb47d315d92b837a0c7db4fafbc38ed099a';
it('sets the password hash', function(done) {
return db.change_password({
account_id: accounts[0],
password_hash: hash,
cb: done
});
});
it('checks that the password is now set', function(done) {
var columns;
columns = ['password_is_set'];
return db.get_account({
account_id: accounts[0],
columns: columns,
cb: function(err, x) {
expect(x).toEqual({
password_is_set: true
});
expect(columns).toEqual(['password_is_set']); // ensure no mutation
return done(err);
}
});
});
it('calls get_account with some columns again', function(done) {
return db.get_account({
account_id: accounts[0],
columns: ['account_id', 'email_address', 'password_hash'],
cb: function(err, x) {
expect(x).toEqual({
account_id: accounts[0],
email_address: "sage+0@sagemath.com",
password_hash: hash
});
return done(err);
}
});
});
it('calls get_account with some columns yet again', function(done) {
return db.get_account({
account_id: accounts[0],
columns: ['account_id', 'email_address', 'password_hash', 'password_is_set'],
cb: function(err, x) {
expect(x).toEqual({
account_id: accounts[0],
email_address: "sage+0@sagemath.com",
password_hash: hash,
password_is_set: true
});
return done(err);
}
});
});
it('calls get_account on the other account', function(done) {
return db.get_account({
account_id: accounts[1],
columns: ['account_id', 'email_address', 'password_hash', 'password_is_set'],
cb: function(err, x) {
expect(x).toEqual({
account_id: accounts[1],
email_address: "sage+1@sagemath.com",
password_is_set: false
});
return done(err);
}
});
});
it('changes the email address of the first account', function(done) {
return db.change_email_address({
account_id: accounts[0],
email_address: 'awesome@sagemath.com',
cb: done
});
});
return it('confirms the change', function(done) {
return db.get_account({
account_id: accounts[0],
columns: ['email_address'],
cb: function(err, x) {
expect(x).toEqual({
email_address: "awesome@sagemath.com"
});
return done(err);
}
});
});
});
describe('test of automatic first and last name truncation', function() {
var _, account_id, long_first, long_last;
before(setup);
after(teardown);
account_id = void 0;
it('creates an account', function(done) {
return db.create_account({
first_name: "Sage",
last_name: "Math",
created_by: "1.2.3.4",
email_address: "sage@example.com",
password_hash: "blah",
cb: function(err, x) {
account_id = x;
return done(err);
}
});
});
long_first = ((function() {
var i, results;
results = [];
for (_ = i = 0; i <= 15; _ = ++i) {
results.push(Math.random().toString(36));
}
return results;
})()).join('');
long_last = ((function() {
var i, results;
results = [];
for (_ = i = 0; i <= 15; _ = ++i) {
results.push(Math.random().toString(36));
}
return results;
})()).join('');
it('sets first_name and last_name to long character strings', function(done) {
return db.user_query({
account_id: account_id,
query: {
accounts: {
account_id: account_id,
first_name: long_first,
last_name: long_last
}
},
cb: done
});
});
// NOTE: this is entirely to prevent malicious/idiotic clients.
return it('reads back and sees they were (silently!) truncated to 254 characters', function(done) {
return db.user_query({
account_id: account_id,
query: {
accounts: {
account_id: account_id,
first_name: null,
last_name: null
}
},
cb: function(err, x) {
expect(x != null ? x.accounts : void 0).toEqual({
account_id: account_id,
first_name: long_first.slice(0, 254),
last_name: long_last.slice(0, 254)
});
return done(err);
}
});
});
});
}).call(this);
//# sourceMappingURL=postgres-user-queries-basic.js.map