UNPKG

smc-hub

Version:

CoCalc: Backend webserver component

1,315 lines (1,296 loc) 35.2 kB
// 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