// Tests that users can only use transactions that they created.
// @tags: [uses_transactions]
import {ReplSetTest} from "jstests/libs/replsettest.js";

const rst = new ReplSetTest({nodes: 1, keyFile: "jstests/libs/key1"});
rst.startSet();
rst.initiate();

const adminDB = rst.getPrimary().getDB("admin");

// Create the admin user.
assert.commandWorked(adminDB.runCommand({createUser: "admin", pwd: "admin", roles: ["root"]}));
assert.eq(1, adminDB.auth("admin", "admin"));

// Set up the test database.
const dbName = "test";
const collName = "transactions";
const testDB = adminDB.getSiblingDB(dbName);
testDB.dropDatabase();
assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}}));

// Create two users, "Alice" and "Mallory".
assert.commandWorked(testDB.runCommand({createUser: "Alice", pwd: "pwd", roles: ["readWrite"]}));
assert.commandWorked(testDB.runCommand({createUser: "Mallory", pwd: "pwd", roles: ["readWrite"]}));
adminDB.logout();

// Alice starts a transaction.
assert.eq(1, testDB.auth("Alice", "pwd"));
const lsid = assert.commandWorked(testDB.runCommand({startSession: 1})).id;
assert.commandWorked(testDB.runCommand({
    insert: collName,
    documents: [{_id: "alice-1"}],
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(0),
    startTransaction: true,
    autocommit: false
}));
testDB.logout();

// Mallory cannot continue the transaction. Using the same lsid for two different users creates
// two distinct sessions on the server. Mallory's session does not have an open transaction.
assert.eq(1, testDB.auth("Mallory", "pwd"));
assert.commandFailedWithCode(testDB.runCommand({
    insert: collName,
    documents: [{_id: "mallory"}],
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false
}),
                             ErrorCodes.NoSuchTransaction);

// Mallory cannot commit the transaction.
assert.commandFailedWithCode(adminDB.runCommand({
    commitTransaction: 1,
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false,
    writeConcern: {w: "majority"}
}),
                             ErrorCodes.NoSuchTransaction);

// Mallory cannot abort the transaction.
assert.commandFailedWithCode(adminDB.runCommand({
    abortTransaction: 1,
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false,
    writeConcern: {w: "majority"}
}),
                             ErrorCodes.NoSuchTransaction);
testDB.logout();

// An unauthenticated user cannot continue the transaction.
assert.commandFailedWithCode(testDB.runCommand({
    insert: collName,
    documents: [{_id: "unauthenticated"}],
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false
}),
                             ErrorCodes.Unauthorized);

// An unauthenticated user cannot commit the transaction.
assert.commandFailedWithCode(adminDB.runCommand({
    commitTransaction: 1,
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false,
    writeConcern: {w: "majority"}
}),
                             ErrorCodes.Unauthorized);

// An unauthenticated user cannot abort the transaction.
assert.commandFailedWithCode(adminDB.runCommand({
    abortTransaction: 1,
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false,
    writeConcern: {w: "majority"}
}),
                             ErrorCodes.Unauthorized);

// Alice can continue the transaction.
assert.eq(1, testDB.auth("Alice", "pwd"));
assert.commandWorked(testDB.runCommand({
    insert: collName,
    documents: [{_id: "alice-2"}],
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(1),
    autocommit: false
}));

// Alice can commit the transaction.
assert.commandWorked(adminDB.runCommand({
    commitTransaction: 1,
    lsid: lsid,
    txnNumber: NumberLong(0),
    stmtId: NumberInt(2),
    autocommit: false,
    writeConcern: {w: "majority"}
}));

// We do not see the writes from Mallory or the unauthenticated user.
assert.eq(1, testDB[collName].find({_id: "alice-1"}).itcount());
assert.eq(1, testDB[collName].find({_id: "alice-2"}).itcount());
assert.eq(0, testDB[collName].find({_id: "mallory"}).itcount());
assert.eq(0, testDB[collName].find({_id: "unauthenticated"}).itcount());

assert.commandWorked(testDB.runCommand({endSessions: [lsid]}));
testDB.logout();
rst.stopSet();