// This is a port of the test suite: // hungry/js/test/parse_user_test.js // // Things that we didn't port: // Tests that involve revocable sessions. // Tests that involve sending password reset emails. "use strict"; var request = require('request'); var passwordCrypto = require('../src/password'); var Config = require('../src/Config'); const rp = require('request-promise'); function verifyACL(user) { const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); expect(ACL.getPublicReadAccess()).toBe(true); expect(ACL.getPublicWriteAccess()).toBe(false); const perms = ACL.permissionsById; expect(Object.keys(perms).length).toBe(2); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); expect(perms['*'].read).toBe(true); expect(perms['*'].write).not.toBe(true); } describe('Parse.User testing', () => { it("user sign up class method", (done) => { Parse.User.signUp("asdf", "zxcv", null, { success: function(user) { ok(user.getSessionToken()); done(); } }); }); it("user sign up instance method", (done) => { var user = new Parse.User(); user.setPassword("asdf"); user.setUsername("zxcv"); user.signUp(null, { success: function(user) { ok(user.getSessionToken()); done(); }, error: function(userAgain, error) { ok(undefined, error); } }); }); it("user login wrong username", (done) => { Parse.User.signUp("asdf", "zxcv", null, { success: function() { Parse.User.logIn("non_existent_user", "asdf3", expectError(Parse.Error.OBJECT_NOT_FOUND, done)); }, error: function(err) { jfail(err); fail("Shit should not fail"); done(); } }); }); it("user login wrong password", (done) => { Parse.User.signUp("asdf", "zxcv", null, { success: function() { Parse.User.logIn("asdf", "asdfWrong", expectError(Parse.Error.OBJECT_NOT_FOUND, done)); } }); }); it('user login with non-string username with REST API', (done) => { Parse.User.signUp('asdf', 'zxcv', null, { success: () => { return rp.post({ url: 'http://localhost:8378/1/login', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: { _method: 'GET', username: {'$regex':'^asd'}, password: 'zxcv', } }).then((res) => { fail(`no request should succeed: ${JSON.stringify(res)}`); done(); }).catch((err) => { expect(err.statusCode).toBe(404); expect(err.message).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }); }, }); }); it('user login with non-string username with REST API', (done) => { Parse.User.signUp('asdf', 'zxcv', null, { success: () => { return rp.post({ url: 'http://localhost:8378/1/login', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: { _method: 'GET', username: 'asdf', password: {'$regex':'^zx'}, } }).then((res) => { fail(`no request should succeed: ${JSON.stringify(res)}`); done(); }).catch((err) => { expect(err.statusCode).toBe(404); expect(err.message).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }); }, }); }); it('user login using POST with REST API', (done) => { Parse.User.signUp('some_user', 'some_password', null, { success: () => { return rp.post({ url: 'http://localhost:8378/1/login', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: { username: 'some_user', password: 'some_password', } }).then((res) => { expect(res.username).toBe('some_user'); done(); }).catch((err) => { fail(`no request should fail: ${JSON.stringify(err)}`); done(); }); }, }); }); it("user login", (done) => { Parse.User.signUp("asdf", "zxcv", null, { success: function() { Parse.User.logIn("asdf", "zxcv", { success: function(user) { equal(user.get("username"), "asdf"); verifyACL(user); done(); } }); } }); }); it('should respect ACL without locking user out', (done) => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); user.setUsername('asdf'); user.setPassword('zxcv'); user.setACL(ACL); user.signUp().then(() => { return Parse.User.logIn("asdf", "zxcv"); }).then((user) => { equal(user.get("username"), "asdf"); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); expect(ACL.getPublicReadAccess()).toBe(false); expect(ACL.getPublicWriteAccess()).toBe(false); const perms = ACL.permissionsById; expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); expect(perms['*']).toBeUndefined(); // Try to lock out user const newACL = new Parse.ACL(); newACL.setReadAccess(user.id, false); newACL.setWriteAccess(user.id, false); user.setACL(newACL); return user.save(); }).then(() => { return Parse.User.logIn("asdf", "zxcv"); }).then((user) => { equal(user.get("username"), "asdf"); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); expect(ACL.getPublicReadAccess()).toBe(false); expect(ACL.getPublicWriteAccess()).toBe(false); const perms = ACL.permissionsById; expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); expect(perms['*']).toBeUndefined(); done(); }).catch(() => { fail("Should not fail"); done(); }) }); it("user login with files", (done) => { const file = new Parse.File("yolo.txt", [1,2,3], "text/plain"); file.save().then((file) => { return Parse.User.signUp("asdf", "zxcv", { "file" : file }); }).then(() => { return Parse.User.logIn("asdf", "zxcv"); }).then((user) => { const fileAgain = user.get('file'); ok(fileAgain.name()); ok(fileAgain.url()); done(); }).catch(err => { jfail(err); done(); }); }); it('become sends token back', done => { let user = null; var sessionToken = null; Parse.User.signUp('Jason', 'Parse', { 'code': 'red' }).then(newUser => { user = newUser; expect(user.get('code'), 'red'); sessionToken = newUser.getSessionToken(); expect(sessionToken).toBeDefined(); return Parse.User.become(sessionToken); }).then(newUser => { expect(newUser.id).toEqual(user.id); expect(newUser.get('username'), 'Jason'); expect(newUser.get('code'), 'red'); expect(newUser.getSessionToken()).toEqual(sessionToken); }).then(() => { done(); }, error => { jfail(error); done(); }); }); it("become", (done) => { var user = null; var sessionToken = null; Parse.Promise.as().then(function() { return Parse.User.signUp("Jason", "Parse", { "code": "red" }); }).then(function(newUser) { equal(Parse.User.current(), newUser); user = newUser; sessionToken = newUser.getSessionToken(); ok(sessionToken); return Parse.User.logOut(); }).then(() => { ok(!Parse.User.current()); return Parse.User.become(sessionToken); }).then(function(newUser) { equal(Parse.User.current(), newUser); ok(newUser); equal(newUser.id, user.id); equal(newUser.get("username"), "Jason"); equal(newUser.get("code"), "red"); return Parse.User.logOut(); }).then(() => { ok(!Parse.User.current()); return Parse.User.become("somegarbage"); }).then(function() { // This should have failed actually. ok(false, "Shouldn't have been able to log in with garbage session token."); }, function(error) { ok(error); // Handle the error. return Parse.Promise.as(); }).then(function() { done(); }, function(error) { ok(false, error); done(); }); }); it("cannot save non-authed user", (done) => { var user = new Parse.User(); user.set({ "password": "asdf", "email": "asdf@example.com", "username": "zxcv" }); user.signUp(null, { success: function(userAgain) { equal(userAgain, user); var query = new Parse.Query(Parse.User); query.get(user.id, { success: function(userNotAuthed) { user = new Parse.User(); user.set({ "username": "hacker", "password": "password" }); user.signUp(null, { success: function(userAgain) { equal(userAgain, user); userNotAuthed.set("username", "changed"); userNotAuthed.save().then(fail, (err) => { expect(err.code).toEqual(Parse.Error.SESSION_MISSING); done(); }); }, error: function(model, error) { ok(undefined, error); } }); }, error: function(model, error) { ok(undefined, error); } }); } }); }); it("cannot delete non-authed user", (done) => { var user = new Parse.User(); user.signUp({ "password": "asdf", "email": "asdf@example.com", "username": "zxcv" }, { success: function() { var query = new Parse.Query(Parse.User); query.get(user.id, { success: function(userNotAuthed) { user = new Parse.User(); user.signUp({ "username": "hacker", "password": "password" }, { success: function(userAgain) { equal(userAgain, user); userNotAuthed.set("username", "changed"); userNotAuthed.destroy(expectError( Parse.Error.SESSION_MISSING, done)); } }); } }); } }); }); it("cannot saveAll with non-authed user", (done) => { var user = new Parse.User(); user.signUp({ "password": "asdf", "email": "asdf@example.com", "username": "zxcv" }, { success: function() { var query = new Parse.Query(Parse.User); query.get(user.id, { success: function(userNotAuthed) { user = new Parse.User(); user.signUp({ username: "hacker", password: "password" }, { success: function() { query.get(user.id, { success: function(userNotAuthedNotChanged) { userNotAuthed.set("username", "changed"); var object = new TestObject(); object.save({ user: userNotAuthedNotChanged }, { success: function() { var item1 = new TestObject(); item1.save({ number: 0 }, { success: function(item1) { item1.set("number", 1); var item2 = new TestObject(); item2.set("number", 2); Parse.Object.saveAll( [item1, item2, userNotAuthed], expectError(Parse.Error.SESSION_MISSING, done)); } }); } }); } }); } }); } }); } }); }); it("current user", (done) => { var user = new Parse.User(); user.set("password", "asdf"); user.set("email", "asdf@example.com"); user.set("username", "zxcv"); user.signUp().then(() => { var currentUser = Parse.User.current(); equal(user.id, currentUser.id); ok(user.getSessionToken()); var currentUserAgain = Parse.User.current(); // should be the same object equal(currentUser, currentUserAgain); // test logging out the current user return Parse.User.logOut(); }).then(() => { equal(Parse.User.current(), null); done(); }); }); it("user.isCurrent", (done) => { var user1 = new Parse.User(); var user2 = new Parse.User(); var user3 = new Parse.User(); user1.set("username", "a"); user2.set("username", "b"); user3.set("username", "c"); user1.set("password", "password"); user2.set("password", "password"); user3.set("password", "password"); user1.signUp().then(() => { equal(user1.isCurrent(), true); equal(user2.isCurrent(), false); equal(user3.isCurrent(), false); return user2.signUp(); }).then(() => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), true); equal(user3.isCurrent(), false); return user3.signUp(); }).then(() => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), false); equal(user3.isCurrent(), true); return Parse.User.logIn("a", "password"); }).then(() => { equal(user1.isCurrent(), true); equal(user2.isCurrent(), false); equal(user3.isCurrent(), false); return Parse.User.logIn("b", "password"); }).then(() => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), true); equal(user3.isCurrent(), false); return Parse.User.logIn("b", "password"); }).then(() => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), true); equal(user3.isCurrent(), false); return Parse.User.logOut(); }).then(() => { equal(user2.isCurrent(), false); done(); }); }); it("user associations", (done) => { var child = new TestObject(); child.save(null, { success: function() { var user = new Parse.User(); user.set("password", "asdf"); user.set("email", "asdf@example.com"); user.set("username", "zxcv"); user.set("child", child); user.signUp(null, { success: function() { var object = new TestObject(); object.set("user", user); object.save(null, { success: function() { var query = new Parse.Query(TestObject); query.get(object.id, { success: function(objectAgain) { var userAgain = objectAgain.get("user"); userAgain.fetch({ success: function() { equal(user.id, userAgain.id); equal(userAgain.get("child").id, child.id); done(); } }); } }); } }); } }); } }); }); it("user queries", (done) => { var user = new Parse.User(); user.set("password", "asdf"); user.set("email", "asdf@example.com"); user.set("username", "zxcv"); user.signUp(null, { success: function() { var query = new Parse.Query(Parse.User); query.get(user.id, { success: function(userAgain) { equal(userAgain.id, user.id); query.find({ success: function(users) { equal(users.length, 1); equal(users[0].id, user.id); ok(userAgain.get("email"), "asdf@example.com"); done(); } }); } }); } }); }); function signUpAll(list, optionsOrCallback) { var promise = Parse.Promise.as(); list.forEach((user) => { promise = promise.then(function() { return user.signUp(); }); }); promise = promise.then(function() { return list; }); return promise._thenRunCallbacks(optionsOrCallback); } it("contained in user array queries", (done) => { var USERS = 4; var MESSAGES = 5; // Make a list of users. var userList = range(USERS).map(function(i) { var user = new Parse.User(); user.set("password", "user_num_" + i); user.set("email", "user_num_" + i + "@example.com"); user.set("username", "xinglblog_num_" + i); return user; }); signUpAll(userList, function(users) { // Make a list of messages. if (!users || users.length != USERS) { fail('signupAll failed'); done(); return; } var messageList = range(MESSAGES).map(function(i) { var message = new TestObject(); message.set("to", users[(i + 1) % USERS]); message.set("from", users[i % USERS]); return message; }); // Save all the messages. Parse.Object.saveAll(messageList, function() { // Assemble an "in" list. var inList = [users[0], users[3], users[3]]; // Intentional dupe var query = new Parse.Query(TestObject); query.containedIn("from", inList); query.find({ success: function(results) { equal(results.length, 3); done(); } }); }); }); }); it("saving a user signs them up but doesn't log them in", (done) => { var user = new Parse.User(); user.save({ password: "asdf", email: "asdf@example.com", username: "zxcv" }, { success: function() { equal(Parse.User.current(), null); done(); } }); }); it("user updates", (done) => { var user = new Parse.User(); user.signUp({ password: "asdf", email: "asdf@example.com", username: "zxcv" }, { success: function(user) { user.set("username", "test"); user.save(null, { success: function() { equal(Object.keys(user.attributes).length, 6); ok(user.attributes["username"]); ok(user.attributes["email"]); user.destroy({ success: function() { var query = new Parse.Query(Parse.User); query.get(user.id, { error: function(model, error) { // The user should no longer exist. equal(error.code, Parse.Error.OBJECT_NOT_FOUND); done(); } }); }, error: function(model, error) { ok(undefined, error); } }); }, error: function(model, error) { ok(undefined, error); } }); }, error: function(model, error) { ok(undefined, error); } }); }); it("count users", (done) => { var james = new Parse.User(); james.set("username", "james"); james.set("password", "mypass"); james.signUp(null, { success: function() { var kevin = new Parse.User(); kevin.set("username", "kevin"); kevin.set("password", "mypass"); kevin.signUp(null, { success: function() { var query = new Parse.Query(Parse.User); query.count({ success: function(count) { equal(count, 2); done(); } }); } }); } }); }); it("user sign up with container class", (done) => { Parse.User.signUp("ilya", "mypass", { "array": ["hello"] }, { success: function() { done(); } }); }); it("user modified while saving", (done) => { Parse.Object.disableSingleInstance(); var user = new Parse.User(); user.set("username", "alice"); user.set("password", "password"); user.signUp(null, { success: function(userAgain) { equal(userAgain.get("username"), "bob"); ok(userAgain.dirty("username")); var query = new Parse.Query(Parse.User); query.get(user.id, { success: function(freshUser) { equal(freshUser.id, user.id); equal(freshUser.get("username"), "alice"); Parse.Object.enableSingleInstance(); done(); } }); } }); ok(user.set("username", "bob")); }); it("user modified while saving with unsaved child", (done) => { Parse.Object.disableSingleInstance(); var user = new Parse.User(); user.set("username", "alice"); user.set("password", "password"); user.set("child", new TestObject()); user.signUp(null, { success: function(userAgain) { equal(userAgain.get("username"), "bob"); // Should be dirty, but it depends on batch support. // ok(userAgain.dirty("username")); var query = new Parse.Query(Parse.User); query.get(user.id, { success: function(freshUser) { equal(freshUser.id, user.id); // Should be alice, but it depends on batch support. equal(freshUser.get("username"), "bob"); Parse.Object.enableSingleInstance(); done(); } }); } }); ok(user.set("username", "bob")); }); it("user loaded from localStorage from signup", (done) => { Parse.User.signUp("alice", "password", null, { success: function(alice) { ok(alice.id, "Alice should have an objectId"); ok(alice.getSessionToken(), "Alice should have a session token"); equal(alice.get("password"), undefined, "Alice should not have a password"); // Simulate the environment getting reset. Parse.User._currentUser = null; Parse.User._currentUserMatchesDisk = false; var aliceAgain = Parse.User.current(); equal(aliceAgain.get("username"), "alice"); equal(aliceAgain.id, alice.id, "currentUser should have objectId"); ok(aliceAgain.getSessionToken(), "currentUser should have a sessionToken"); equal(alice.get("password"), undefined, "currentUser should not have password"); done(); } }); }); it("user loaded from localStorage from login", (done) => { var id; Parse.User.signUp("alice", "password").then((alice) => { id = alice.id; return Parse.User.logOut(); }).then(() => { return Parse.User.logIn("alice", "password"); }).then(() => { // Force the current user to read from disk delete Parse.User._currentUser; delete Parse.User._currentUserMatchesDisk; var userFromDisk = Parse.User.current(); equal(userFromDisk.get("password"), undefined, "password should not be in attributes"); equal(userFromDisk.id, id, "id should be set"); ok(userFromDisk.getSessionToken(), "currentUser should have a sessionToken"); done(); }); }); it("saving user after browser refresh", (done) => { var id; Parse.User.signUp("alice", "password", null).then(function(alice) { id = alice.id; return Parse.User.logOut(); }).then(() => { return Parse.User.logIn("alice", "password"); }).then(function() { // Simulate browser refresh by force-reloading user from localStorage Parse.User._clearCache(); // Test that this save works correctly return Parse.User.current().save({some_field: 1}); }).then(function() { // Check the user in memory just after save operation var userInMemory = Parse.User.current(); equal(userInMemory.getUsername(), "alice", "saving user should not remove existing fields"); equal(userInMemory.get('some_field'), 1, "saving user should save specified field"); equal(userInMemory.get("password"), undefined, "password should not be in attributes after saving user"); equal(userInMemory.get("objectId"), undefined, "objectId should not be in attributes after saving user"); equal(userInMemory.get("_id"), undefined, "_id should not be in attributes after saving user"); equal(userInMemory.id, id, "id should be set"); expect(userInMemory.updatedAt instanceof Date).toBe(true); ok(userInMemory.createdAt instanceof Date); ok(userInMemory.getSessionToken(), "user should have a sessionToken after saving"); // Force the current user to read from localStorage, and check again delete Parse.User._currentUser; delete Parse.User._currentUserMatchesDisk; var userFromDisk = Parse.User.current(); equal(userFromDisk.getUsername(), "alice", "userFromDisk should have previously existing fields"); equal(userFromDisk.get('some_field'), 1, "userFromDisk should have saved field"); equal(userFromDisk.get("password"), undefined, "password should not be in attributes of userFromDisk"); equal(userFromDisk.get("objectId"), undefined, "objectId should not be in attributes of userFromDisk"); equal(userFromDisk.get("_id"), undefined, "_id should not be in attributes of userFromDisk"); equal(userFromDisk.id, id, "id should be set on userFromDisk"); ok(userFromDisk.updatedAt instanceof Date); ok(userFromDisk.createdAt instanceof Date); ok(userFromDisk.getSessionToken(), "userFromDisk should have a sessionToken"); done(); }, function(error) { ok(false, error); done(); }); }); it("user with missing username", (done) => { var user = new Parse.User(); user.set("password", "foo"); user.signUp(null, { success: function() { ok(null, "This should have failed"); done(); }, error: function(userAgain, error) { equal(error.code, Parse.Error.OTHER_CAUSE); done(); } }); }); it("user with missing password", (done) => { var user = new Parse.User(); user.set("username", "foo"); user.signUp(null, { success: function() { ok(null, "This should have failed"); done(); }, error: function(userAgain, error) { equal(error.code, Parse.Error.OTHER_CAUSE); done(); } }); }); it("user stupid subclassing", (done) => { var SuperUser = Parse.Object.extend("User"); var user = new SuperUser(); user.set("username", "bob"); user.set("password", "welcome"); ok(user instanceof Parse.User, "Subclassing User should have worked"); user.signUp(null, { success: function() { done(); }, error: function() { ok(false, "Signing up should have worked"); done(); } }); }); it("user signup class method uses subclassing", (done) => { var SuperUser = Parse.User.extend({ secret: function() { return 1337; } }); Parse.User.signUp("bob", "welcome", null, { success: function(user) { ok(user instanceof SuperUser, "Subclassing User should have worked"); equal(user.secret(), 1337); done(); }, error: function() { ok(false, "Signing up should have worked"); done(); } }); }); it("user on disk gets updated after save", (done) => { Parse.User.extend({ isSuper: function() { return true; } }); Parse.User.signUp("bob", "welcome", null, { success: function(user) { // Modify the user and save. user.save("secret", 1337, { success: function() { // Force the current user to read from disk delete Parse.User._currentUser; delete Parse.User._currentUserMatchesDisk; var userFromDisk = Parse.User.current(); equal(userFromDisk.get("secret"), 1337); ok(userFromDisk.isSuper(), "The subclass should have been used"); done(); }, error: function() { ok(false, "Saving should have worked"); done(); } }); }, error: function() { ok(false, "Sign up should have worked"); done(); } }); }); it("current user isn't dirty", (done) => { Parse.User.signUp("andrew", "oppa", { style: "gangnam" }, expectSuccess({ success: function(user) { ok(!user.dirty("style"), "The user just signed up."); Parse.User._currentUser = null; Parse.User._currentUserMatchesDisk = false; var userAgain = Parse.User.current(); ok(!userAgain.dirty("style"), "The user was just read from disk."); done(); } })); }); var getMockFacebookProviderWithIdToken = function(id, token) { return { authData: { id: id, access_token: token, expiration_date: new Date().toJSON(), }, shouldError: false, loggedOut: false, synchronizedUserId: null, synchronizedAuthToken: null, synchronizedExpiration: null, authenticate: function(options) { if (this.shouldError) { options.error(this, "An error occurred"); } else if (this.shouldCancel) { options.error(this, null); } else { options.success(this, this.authData); } }, restoreAuthentication: function(authData) { if (!authData) { this.synchronizedUserId = null; this.synchronizedAuthToken = null; this.synchronizedExpiration = null; return true; } this.synchronizedUserId = authData.id; this.synchronizedAuthToken = authData.access_token; this.synchronizedExpiration = authData.expiration_date; return true; }, getAuthType: function() { return "facebook"; }, deauthenticate: function() { this.loggedOut = true; this.restoreAuthentication(null); } }; } // Note that this mocks out client-side Facebook action rather than // server-side. var getMockFacebookProvider = function() { return getMockFacebookProviderWithIdToken('8675309', 'jenny'); }; var getMockMyOauthProvider = function() { return { authData: { id: "12345", access_token: "12345", expiration_date: new Date().toJSON(), }, shouldError: false, loggedOut: false, synchronizedUserId: null, synchronizedAuthToken: null, synchronizedExpiration: null, authenticate: function(options) { if (this.shouldError) { options.error(this, "An error occurred"); } else if (this.shouldCancel) { options.error(this, null); } else { options.success(this, this.authData); } }, restoreAuthentication: function(authData) { if (!authData) { this.synchronizedUserId = null; this.synchronizedAuthToken = null; this.synchronizedExpiration = null; return true; } this.synchronizedUserId = authData.id; this.synchronizedAuthToken = authData.access_token; this.synchronizedExpiration = authData.expiration_date; return true; }, getAuthType: function() { return "myoauth"; }, deauthenticate: function() { this.loggedOut = true; this.restoreAuthentication(null); } }; }; Parse.User.extend({ extended: function() { return true; } }); it("log in with provider", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook"); done(); }, error: function(model, error) { jfail(error); ok(false, "linking should have worked"); done(); } }); }); it("user authData should be available in cloudcode (#2342)", (done) => { Parse.Cloud.define('checkLogin', (req, res) => { expect(req.user).not.toBeUndefined(); expect(Parse.FacebookUtils.isLinked(req.user)).toBe(true); res.success(); }); var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.Cloud.run('checkLogin').then(done, done); }, error: function(model, error) { jfail(error); ok(false, "linking should have worked"); done(); } }); }); it("log in with provider and update token", (done) => { var provider = getMockFacebookProvider(); var secondProvider = getMockFacebookProviderWithIdToken('8675309', 'jenny_valid_token'); var errorHandler = function() { fail('should not fail'); done(); } Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: () => { Parse.User._registerAuthenticationProvider(secondProvider); return Parse.User.logOut().then(() => { Parse.User._logInWith("facebook", { success: () => { expect(secondProvider.synchronizedAuthToken).toEqual('jenny_valid_token'); // Make sure we can login with the new token again Parse.User.logOut().then(() => { Parse.User._logInWith("facebook", { success: done, error: errorHandler }); }); }, error: errorHandler }); }) }, error: errorHandler }).catch((err) => { errorHandler(err); done(); }); }); it('returns authData when authed and logged in with provider (regression test for #1498)', done => { Parse.Object.enableSingleInstance(); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith('facebook', { success: user => { const userQuery = new Parse.Query(Parse.User); userQuery.get(user.id) .then(user => { expect(user.get('authData')).not.toBeUndefined(); Parse.Object.disableSingleInstance(); done(); }); } }); }); it('only creates a single session for an installation / user pair (#2885)', done => { Parse.Object.disableSingleInstance(); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User.logInWith('facebook', { success: () => { return Parse.User.logInWith('facebook', { success: () => { return Parse.User.logInWith('facebook', { success: (user) => { const sessionToken = user.getSessionToken(); const query = new Parse.Query('_Session'); return query.find({ useMasterKey: true }) .then((results) => { expect(results.length).toBe(1); expect(results[0].get('sessionToken')).toBe(sessionToken); expect(results[0].get('createdWith')).toEqual({ action: 'login', authProvider: 'facebook' }); done(); }).catch(done.fail); } }); } }); } }); }); it('log in with provider with files', done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); file.save().then(file => { const user = new Parse.User(); user.set('file', file); return user._linkWith('facebook', {}); }).then(user => { expect(user._isLinked("facebook")).toBeTruthy(); return Parse.User._logInWith('facebook', {}); }).then(user => { const fileAgain = user.get('file'); expect(fileAgain.name()).toMatch(/yolo.txt$/); expect(fileAgain.url()).toMatch(/yolo.txt$/); }).then(() => { done(); }).catch(done.fail); }); it("log in with provider twice", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.User.logOut().then(() => { ok(provider.loggedOut); provider.loggedOut = false; Parse.User._logInWith("facebook", { success: function(innerModel) { ok(innerModel instanceof Parse.User, "Model should be a Parse.User"); ok(innerModel === Parse.User.current(), "Returned model should be the current user"); ok(provider.authData.id === provider.synchronizedUserId); ok(provider.authData.access_token === provider.synchronizedAuthToken); ok(innerModel._isLinked("facebook"), "User should be linked to facebook"); ok(innerModel.existed(), "User should not be newly-created"); done(); }, error: function(model, error) { jfail(error); ok(false, "LogIn should have worked"); done(); } }); }, done.fail); }, error: function(model, error) { jfail(error); ok(false, "LogIn should have worked"); done(); } }); }); it("log in with provider failed", (done) => { var provider = getMockFacebookProvider(); provider.shouldError = true; Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function() { ok(false, "logIn should not have succeeded"); }, error: function(model, error) { ok(error, "Error should be non-null"); done(); } }); }); it("log in with provider cancelled", (done) => { var provider = getMockFacebookProvider(); provider.shouldCancel = true; Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function() { ok(false, "logIn should not have succeeded"); }, error: function(model, error) { ok(error === null, "Error should be null"); done(); } }); }); it("login with provider should not call beforeSave trigger", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function() { Parse.User.logOut().then(() => { Parse.Cloud.beforeSave(Parse.User, function(req, res) { res.error("Before save shouldn't be called on login"); }); Parse.User._logInWith("facebook", { success: function() { done(); }, error: function(model, error) { ok(undefined, error); done(); } }); }); } }); }); it("link with provider", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); var user = new Parse.User(); user.set("username", "testLinkWithProvider"); user.set("password", "mypass"); user.signUp(null, { success: function() { user._linkWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked"); done(); }, error: function() { ok(false, "linking should have succeeded"); done(); } }); }, error: function() { ok(false, "signup should not have failed"); done(); } }); }); // What this means is, only one Parse User can be linked to a // particular Facebook account. it("link with provider for already linked user", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); var user = new Parse.User(); user.set("username", "testLinkWithProviderToAlreadyLinkedUser"); user.set("password", "mypass"); user.signUp(null, { success: function() { user._linkWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked."); var user2 = new Parse.User(); user2.set("username", "testLinkWithProviderToAlreadyLinkedUser2"); user2.set("password", "mypass"); user2.signUp(null, { success: function() { user2._linkWith('facebook', { success: (err) => { jfail(err); done(); }, error: function(model, error) { expect(error.code).toEqual( Parse.Error.ACCOUNT_ALREADY_LINKED); done(); }, }); }, error: function() { ok(false, "linking should have failed"); done(); } }); }, error: function() { ok(false, "linking should have succeeded"); done(); } }); }, error: function() { ok(false, "signup should not have failed"); done(); } }); }); it("link with provider failed", (done) => { var provider = getMockFacebookProvider(); provider.shouldError = true; Parse.User._registerAuthenticationProvider(provider); var user = new Parse.User(); user.set("username", "testLinkWithProvider"); user.set("password", "mypass"); user.signUp(null, { success: function() { user._linkWith("facebook", { success: function() { ok(false, "linking should fail"); done(); }, error: function(model, error) { ok(error, "Linking should fail"); ok(!model._isLinked("facebook"), "User should not be linked to facebook"); done(); } }); }, error: function() { ok(false, "signup should not have failed"); done(); } }); }); it("link with provider cancelled", (done) => { var provider = getMockFacebookProvider(); provider.shouldCancel = true; Parse.User._registerAuthenticationProvider(provider); var user = new Parse.User(); user.set("username", "testLinkWithProvider"); user.set("password", "mypass"); user.signUp(null, { success: function() { user._linkWith("facebook", { success: function() { ok(false, "linking should fail"); done(); }, error: function(model, error) { ok(!error, "Linking should be cancelled"); ok(!model._isLinked("facebook"), "User should not be linked to facebook"); done(); } }); }, error: function() { ok(false, "signup should not have failed"); done(); } }); }); it("unlink with provider", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User."); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook."); model._unlinkFrom("facebook", { success: function(model) { ok(!model._isLinked("facebook"), "User should not be linked."); ok(!provider.synchronizedUserId, "User id should be cleared."); ok(!provider.synchronizedAuthToken, "Auth token should be cleared."); ok(!provider.synchronizedExpiration, "Expiration should be cleared."); done(); }, error: function() { ok(false, "unlinking should succeed"); done(); } }); }, error: function() { ok(false, "linking should have worked"); done(); } }); }); it("unlink and link", (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook"); model._unlinkFrom("facebook", { success: function(model) { ok(!model._isLinked("facebook"), "User should not be linked to facebook"); ok(!provider.synchronizedUserId, "User id should be cleared"); ok(!provider.synchronizedAuthToken, "Auth token should be cleared"); ok(!provider.synchronizedExpiration, "Expiration should be cleared"); model._linkWith("facebook", { success: function(model) { ok(provider.synchronizedUserId, "User id should have a value"); ok(provider.synchronizedAuthToken, "Auth token should have a value"); ok(provider.synchronizedExpiration, "Expiration should have a value"); ok(model._isLinked("facebook"), "User should be linked to facebook"); done(); }, error: function() { ok(false, "linking again should succeed"); done(); } }); }, error: function() { ok(false, "unlinking should succeed"); done(); } }); }, error: function() { ok(false, "linking should have worked"); done(); } }); }); it("link multiple providers", (done) => { var provider = getMockFacebookProvider(); var mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; model._linkWith("myoauth", { success: function(model) { expect(model.id).toEqual(objectId); ok(model._isLinked("facebook"), "User should be linked to facebook"); ok(model._isLinked("myoauth"), "User should be linked to myoauth"); done(); }, error: function(error) { jfail(error); fail('SHould not fail'); done(); } }) }, error: function() { ok(false, "linking should have worked"); done(); } }); }); it("link multiple providers and updates token", (done) => { var provider = getMockFacebookProvider(); var secondProvider = getMockFacebookProviderWithIdToken('8675309', 'jenny_valid_token'); var errorHandler = function(model, error) { jfail(error); fail('Should not fail'); done(); } var mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; model._linkWith("myoauth", { success: function() { Parse.User._registerAuthenticationProvider(secondProvider); Parse.User.logOut().then(() => { return Parse.User._logInWith("facebook", { success: () => { Parse.User.logOut().then(() => { return Parse.User._logInWith("myoauth", { success: (user) => { expect(user.id).toBe(objectId); done(); } }) }) }, error: errorHandler }); }) }, error: errorHandler }) }, error: errorHandler }); }); it("link multiple providers and update token", (done) => { var provider = getMockFacebookProvider(); var mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; model._linkWith("myoauth", { success: function(model) { expect(model.id).toEqual(objectId); ok(model._isLinked("facebook"), "User should be linked to facebook"); ok(model._isLinked("myoauth"), "User should be linked to myoauth"); model._linkWith("facebook", { success: () => { ok(model._isLinked("facebook"), "User should be linked to facebook"); ok(model._isLinked("myoauth"), "User should be linked to myoauth"); done(); }, error: () => { fail('should link again'); done(); } }) }, error: function(error) { jfail(error); fail('SHould not fail'); done(); } }) }, error: function() { ok(false, "linking should have worked"); done(); } }); }); it('should fail linking with existing', (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function() { Parse.User.logOut().then(() => { const user = new Parse.User(); user.setUsername('user'); user.setPassword('password'); return user.signUp().then(() => { // try to link here user._linkWith('facebook', { success: () => { fail('should not succeed'); done(); }, error: () => { done(); } }); }); }); } }); }); it('should fail linking with existing', (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(model) { const userId = model.id; Parse.User.logOut().then(() => { request.post({ url:Parse.serverURL + '/classes/_User', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, json: {authData: {facebook: provider.authData}} }, (err,res, body) => { // make sure the location header is properly set expect(userId).not.toBeUndefined(); expect(body.objectId).toEqual(userId); expect(res.headers.location).toEqual(Parse.serverURL + '/users/' + userId); done(); }); }); } }); }); it('should allow login with old authData token', (done) => { const provider = { authData: { id: '12345', access_token: 'token' }, restoreAuthentication: function() { return true; }, deauthenticate: function() { provider.authData = {}; }, authenticate: function(options) { options.success(this, provider.authData); }, getAuthType: function() { return "shortLivedAuth"; } } defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('token'); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("shortLivedAuth", {}).then(() => { // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated one, do lock the user out defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); return Parse.User._logInWith("shortLivedAuth", {}); }).then(() => { done(); }, (err) => { done.fail(err); }); }); it('should allow PUT request with stale auth Data', (done) => { const provider = { authData: { id: '12345', access_token: 'token' }, restoreAuthentication: function() { return true; }, deauthenticate: function() { provider.authData = {}; }, authenticate: function(options) { options.success(this, provider.authData); }, getAuthType: function() { return "shortLivedAuth"; } } defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('token'); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("shortLivedAuth", {}).then(() => { // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated one, do lock the user out defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); return rp.put({ url: Parse.serverURL + '/users/' + Parse.User.current().id, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Javascript-Key': Parse.javaScriptKey, 'X-Parse-Session-Token': Parse.User.current().getSessionToken(), 'Content-Type': 'application/json' }, json: { key: 'value', // update a key authData: { // pass the original auth data shortLivedAuth: { id: '12345', access_token: 'token' } } } }) }).then(() => { done(); }, (err) => { done.fail(err); }); }); it('should properly error when password is missing', (done) => { var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function(user) { user.set('username', 'myUser'); user.set('email', 'foo@example.com'); user.save().then(() => { return Parse.User.logOut(); }).then(() => { return Parse.User.logIn('myUser', 'password'); }).then(() => { fail('should not succeed'); done(); }, (err) => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); expect(err.message).toEqual('Invalid username/password.'); done(); }) } }); }); it('should have authData in beforeSave and afterSave', (done) => { Parse.Cloud.beforeSave('_User', (request, response) => { const authData = request.object.get('authData'); expect(authData).not.toBeUndefined(); if (authData) { expect(authData.facebook.id).toEqual('8675309'); expect(authData.facebook.access_token).toEqual('jenny'); } else { fail('authData should be set'); } response.success(); }); Parse.Cloud.afterSave('_User', (request, response) => { const authData = request.object.get('authData'); expect(authData).not.toBeUndefined(); if (authData) { expect(authData.facebook.id).toEqual('8675309'); expect(authData.facebook.access_token).toEqual('jenny'); } else { fail('authData should be set'); } response.success(); }); var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { success: function() { done(); } }); }); it('set password then change password', (done) => { Parse.User.signUp('bob', 'barker').then((bob) => { bob.setPassword('meower'); return bob.save(); }).then(() => { return Parse.User.logIn('bob', 'meower'); }).then((bob) => { expect(bob.getUsername()).toEqual('bob'); done(); }, (e) => { console.log(e); fail(); }); }); it("authenticated check", (done) => { var user = new Parse.User(); user.set("username", "darkhelmet"); user.set("password", "onetwothreefour"); ok(!user.authenticated()); user.signUp(null, expectSuccess({ success: function() { ok(user.authenticated()); done(); } })); }); it("log in with explicit facebook auth data", (done) => { Parse.FacebookUtils.logIn({ id: "8675309", access_token: "jenny", expiration_date: new Date().toJSON() }, expectSuccess({success: done})); }); it("log in async with explicit facebook auth data", (done) => { Parse.FacebookUtils.logIn({ id: "8675309", access_token: "jenny", expiration_date: new Date().toJSON() }).then(function() { done(); }, function(error) { ok(false, error); done(); }); }); it("link with explicit facebook auth data", (done) => { Parse.User.signUp("mask", "open sesame", null, expectSuccess({ success: function(user) { Parse.FacebookUtils.link(user, { id: "8675309", access_token: "jenny", expiration_date: new Date().toJSON() }).then(done, (error) => { jfail(error); done(); }); } })); }); it("link async with explicit facebook auth data", (done) => { Parse.User.signUp("mask", "open sesame", null, expectSuccess({ success: function(user) { Parse.FacebookUtils.link(user, { id: "8675309", access_token: "jenny", expiration_date: new Date().toJSON() }).then(function() { done(); }, function(error) { ok(false, error); done(); }); } })); }); it("async methods", (done) => { var data = { foo: "bar" }; Parse.User.signUp("finn", "human", data).then(function(user) { equal(Parse.User.current(), user); equal(user.get("foo"), "bar"); return Parse.User.logOut(); }).then(function() { return Parse.User.logIn("finn", "human"); }).then(function(user) { equal(user, Parse.User.current()); equal(user.get("foo"), "bar"); return Parse.User.logOut(); }).then(function() { var user = new Parse.User(); user.set("username", "jake"); user.set("password", "dog"); user.set("foo", "baz"); return user.signUp(); }).then(function(user) { equal(user, Parse.User.current()); equal(user.get("foo"), "baz"); user = new Parse.User(); user.set("username", "jake"); user.set("password", "dog"); return user.logIn(); }).then(function(user) { equal(user, Parse.User.current()); equal(user.get("foo"), "baz"); var userAgain = new Parse.User(); userAgain.id = user.id; return userAgain.fetch(); }).then(function(userAgain) { equal(userAgain.get("foo"), "baz"); done(); }); }); it("querying for users doesn't get session tokens", (done) => { Parse.User.signUp("finn", "human", { foo: "bar" }) .then(function() { return Parse.User.logOut(); }).then(() => { var user = new Parse.User(); user.set("username", "jake"); user.set("password", "dog"); user.set("foo", "baz"); return user.signUp(); }).then(function() { return Parse.User.logOut(); }).then(() => { var query = new Parse.Query(Parse.User); return query.find(); }).then(function(users) { equal(users.length, 2); for (var user of users) { ok(!user.getSessionToken(), "user should not have a session token."); } done(); }, function(error) { ok(false, error); done(); }); }); it("querying for users only gets the expected fields", (done) => { Parse.User.signUp("finn", "human", { foo: "bar" }) .then(() => { request.get({ headers: {'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest'}, url: 'http://localhost:8378/1/users', }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(b.results.length).toEqual(1); var user = b.results[0]; expect(Object.keys(user).length).toEqual(6); done(); }); }); }); it('retrieve user data from fetch, make sure the session token hasn\'t changed', (done) => { var user = new Parse.User(); user.setPassword("asdf"); user.setUsername("zxcv"); var currentSessionToken = ""; Parse.Promise.as().then(function() { return user.signUp(); }).then(function(){ currentSessionToken = user.getSessionToken(); return user.fetch(); }).then(function(u){ expect(currentSessionToken).toEqual(u.getSessionToken()); done(); }, function(error) { ok(false, error); done(); }) }); it('user save should fail with invalid email', (done) => { var user = new Parse.User(); user.set('username', 'teste'); user.set('password', 'test'); user.set('email', 'invalid'); user.signUp().then(() => { fail('Should not have been able to save.'); done(); }, (error) => { expect(error.code).toEqual(125); done(); }); }); it('user signup should error if email taken', (done) => { var user = new Parse.User(); user.set('username', 'test1'); user.set('password', 'test'); user.set('email', 'test@test.com'); user.signUp().then(() => { var user2 = new Parse.User(); user2.set('username', 'test2'); user2.set('password', 'test'); user2.set('email', 'test@test.com'); return user2.signUp(); }).then(() => { fail('Should not have been able to sign up.'); done(); }, () => { done(); }); }); it('user cannot update email to existing user', (done) => { var user = new Parse.User(); user.set('username', 'test1'); user.set('password', 'test'); user.set('email', 'test@test.com'); user.signUp().then(() => { var user2 = new Parse.User(); user2.set('username', 'test2'); user2.set('password', 'test'); return user2.signUp(); }).then((user2) => { user2.set('email', 'test@test.com'); return user2.save(); }).then(() => { fail('Should not have been able to sign up.'); done(); }, () => { done(); }); }); it('unset user email', (done) => { var user = new Parse.User(); user.set('username', 'test'); user.set('password', 'test'); user.set('email', 'test@test.com'); user.signUp().then(() => { user.unset('email'); return user.save(); }).then(() => { return Parse.User.logIn('test', 'test'); }).then((user) => { expect(user.getEmail()).toBeUndefined(); done(); }); }); it('create session from user', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("finn", "human", { foo: "bar" }); }).then((user) => { request.post({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions', }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(typeof b.sessionToken).toEqual('string'); expect(typeof b.createdWith).toEqual('object'); expect(b.createdWith.action).toEqual('create'); expect(typeof b.user).toEqual('object'); expect(b.user.objectId).toEqual(user.id); done(); }); }); }); it('user get session from token on signup', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("finn", "human", { foo: "bar" }); }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/me', }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(typeof b.sessionToken).toEqual('string'); expect(typeof b.createdWith).toEqual('object'); expect(b.createdWith.action).toEqual('signup'); expect(typeof b.user).toEqual('object'); expect(b.user.objectId).toEqual(user.id); done(); }); }); }); it('user get session from token on login', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("finn", "human", { foo: "bar" }); }).then(() => { return Parse.User.logOut().then(() => { return Parse.User.logIn("finn", "human"); }) }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/me', }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(typeof b.sessionToken).toEqual('string'); expect(typeof b.createdWith).toEqual('object'); expect(b.createdWith.action).toEqual('login'); expect(typeof b.user).toEqual('object'); expect(b.user.objectId).toEqual(user.id); done(); }); }); }); it('user update session with other field', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("finn", "human", { foo: "bar" }); }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/me', }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); request.put({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/' + b.objectId, body: JSON.stringify({ foo: 'bar' }) }, (error, response, body) => { expect(error).toBe(null); JSON.parse(body); done(); }); }); }); }); it('cannot update session if invalid or no session token', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("finn", "human", { foo: "bar" }); }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/me', }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); request.put({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': 'foo', 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/' + b.objectId, body: JSON.stringify({ foo: 'bar' }) }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(b.error).toBe('invalid session token'); request.put({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/' + b.objectId, body: JSON.stringify({ foo: 'bar' }) }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(b.error).toBe('Session token required.'); done(); }); }); }); }); }); it('get session only for current user', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("test1", "test", { foo: "bar" }); }).then(() => { return Parse.User.signUp("test2", "test", { foo: "bar" }); }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions' }, (error, response, body) => { expect(error).toBe(null); try { var b = JSON.parse(body); expect(b.results.length).toEqual(1); expect(typeof b.results[0].user).toEqual('object'); expect(b.results[0].user.objectId).toEqual(user.id); } catch(e) { jfail(e); } done(); }); }); }); it('delete session by object', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("test1", "test", { foo: "bar" }); }).then(() => { return Parse.User.signUp("test2", "test", { foo: "bar" }); }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions' }, (error, response, body) => { expect(error).toBe(null); var objId; try { var b = JSON.parse(body); expect(b.results.length).toEqual(1); objId = b.results[0].objectId; } catch(e) { jfail(e); done(); return; } request.del({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/' + objId }, (error) => { expect(error).toBe(null); request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions' }, (error, response, body) => { expect(error).toBe(null); var b = JSON.parse(body); expect(b.code).toEqual(209); expect(b.error).toBe('invalid session token'); done(); }); }); }); }); }); it('cannot delete session if no sessionToken', (done) => { Parse.Promise.as().then(() => { return Parse.User.signUp("test1", "test", { foo: "bar" }); }).then(() => { return Parse.User.signUp("test2", "test", { foo: "bar" }); }).then((user) => { request.get({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Session-Token': user.getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions' }, (error, response, body) => { expect(error).toBe(null); var objId; try { var b = JSON.parse(body); expect(b.results.length).toEqual(1); objId = b.results[0].objectId; } catch(e) { jfail(e); done(); return; } request.del({ headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest' }, url: 'http://localhost:8378/1/sessions/' + objId }, (error,response,body) => { var b = JSON.parse(body); expect(b.code).toEqual(209); expect(b.error).toBe('invalid session token'); done(); }); }); }); }); it('password format matches hosted parse', (done) => { var hashed = '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie'; passwordCrypto.compare('test', hashed) .then((pass) => { expect(pass).toBe(true); done(); }, () => { fail('Password format did not match.'); done(); }); }); it('changing password clears sessions', (done) => { var sessionToken = null; Parse.Promise.as().then(function() { return Parse.User.signUp("fosco", "parse"); }).then(function(newUser) { equal(Parse.User.current(), newUser); sessionToken = newUser.getSessionToken(); ok(sessionToken); newUser.set('password', 'facebook'); return newUser.save(); }).then(function() { return Parse.User.become(sessionToken); }).then(function() { fail('Session should have been invalidated'); done(); }, function(err) { expect(err.code).toBe(Parse.Error.INVALID_SESSION_TOKEN); expect(err.message).toBe('invalid session token'); done(); }); }); it('test parse user become', (done) => { var sessionToken = null; Parse.Promise.as().then(function() { return Parse.User.signUp("flessard", "folo",{'foo':1}); }).then(function(newUser) { equal(Parse.User.current(), newUser); sessionToken = newUser.getSessionToken(); ok(sessionToken); newUser.set('foo',2); return newUser.save(); }).then(function() { return Parse.User.become(sessionToken); }).then(function(newUser) { equal(newUser.get('foo'), 2); done(); }, function() { fail('The session should still be valid'); done(); }); }); it('ensure logout works', (done) => { var user = null; var sessionToken = null; Parse.Promise.as().then(function() { return Parse.User.signUp('log', 'out'); }).then((newUser) => { user = newUser; sessionToken = user.getSessionToken(); return Parse.User.logOut(); }).then(() => { user.set('foo', 'bar'); return user.save(null, { sessionToken: sessionToken }); }).then(() => { fail('Save should have failed.'); done(); }, (e) => { expect(e.code).toEqual(Parse.Error.INVALID_SESSION_TOKEN); done(); }); }); it('support user/password signup with empty authData block', (done) => { // The android SDK can send an empty authData object along with username and password. Parse.User.signUp('artof', 'thedeal', { authData: {} }).then(() => { done(); }, () => { fail('Signup should have succeeded.'); done(); }); }); it("session expiresAt correct format", (done) => { Parse.User.signUp("asdf", "zxcv", null, { success: function() { request.get({ url: 'http://localhost:8378/1/classes/_Session', json: true, headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Master-Key': 'test', }, }, (error, response, body) => { expect(body.results[0].expiresAt.__type).toEqual('Date'); done(); }) } }); }); it("invalid session tokens are rejected", (done) => { Parse.User.signUp("asdf", "zxcv", null, { success: function() { request.get({ url: 'http://localhost:8378/1/classes/AClass', json: true, headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Rest-API-Key': 'rest', 'X-Parse-Session-Token': 'text' }, }, (error, response, body) => { expect(body.code).toBe(209); expect(body.error).toBe('invalid session token'); done(); }) } }); }); it_exclude_dbs(['postgres'])('should cleanup null authData keys (regression test for #935)', (done) => { const database = Config.get(Parse.applicationId).database; database.create('_User', { username: 'user', _hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', _auth_data_facebook: null }, {}).then(() => { return new Promise((resolve, reject) => { request.get({ url: 'http://localhost:8378/1/login?username=user&password=test', headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Master-Key': 'test', }, json: true }, (err, res, body) => { if (err) { reject(err); } else { resolve(body); } }) }) }).then((user) => { const authData = user.authData; expect(user.username).toEqual('user'); expect(authData).toBeUndefined(); done(); }).catch(() => { fail('this should not fail'); done(); }) }); it_exclude_dbs(['postgres'])('should not serve null authData keys', (done) => { const database = Config.get(Parse.applicationId).database; database.create('_User', { username: 'user', _hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', _auth_data_facebook: null }, {}).then(() => { return new Parse.Query(Parse.User) .equalTo('username', 'user') .first({useMasterKey: true}); }).then((user) => { const authData = user.get('authData'); expect(user.get('username')).toEqual('user'); expect(authData).toBeUndefined(); done(); }).catch(() => { fail('this should not fail'); done(); }) }); it('should cleanup null authData keys ParseUser update (regression test for #1198, #2252)', (done) => { Parse.Cloud.beforeSave('_User', (req, res) => { req.object.set('foo', 'bar'); res.success(); }); let originalSessionToken; let originalUserId; // Simulate anonymous user save new Promise((resolve, reject) => { request.post({ url: 'http://localhost:8378/1/classes/_User', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: {authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}} }, (err, res, body) => { if (err) { reject(err); } else { resolve(body); } }); }).then((user) => { originalSessionToken = user.sessionToken; originalUserId = user.objectId; // Simulate registration return new Promise((resolve, reject) => { request.put({ url: 'http://localhost:8378/1/classes/_User/' + user.objectId, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Session-Token': user.sessionToken, 'X-Parse-REST-API-Key': 'rest', }, json: { authData: {anonymous: null}, username: 'user', password: 'password', } }, (err, res, body) => { if (err) { reject(err); } else { resolve(body); } }); }); }).then((user) => { expect(typeof user).toEqual('object'); expect(user.authData).toBeUndefined(); expect(user.sessionToken).not.toBeUndefined(); // Session token should have changed expect(user.sessionToken).not.toEqual(originalSessionToken); // test that the sessionToken is valid return new Promise((resolve, reject) => { request.get({ url: 'http://localhost:8378/1/users/me', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Session-Token': user.sessionToken, 'X-Parse-REST-API-Key': 'rest', }, json: true }, (err, res, body) => { expect(body.username).toEqual('user'); expect(body.objectId).toEqual(originalUserId); if (err) { reject(err); } else { resolve(body); } done(); }); }); }).catch((err) => { fail('no request should fail: ' + JSON.stringify(err)); done(); }); }); it('should send email when upgrading from anon', (done) => { let emailCalled = false; let emailOptions; var emailAdapter = { sendVerificationEmail: (options) => { emailOptions = options; emailCalled = true; }, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve() } reconfigureServer({ appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, publicServerURL: "http://localhost:8378/1" }) // Simulate anonymous user save return rp.post({ url: 'http://localhost:8378/1/classes/_User', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: {authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}} }).then((user) => { return rp.put({ url: 'http://localhost:8378/1/classes/_User/' + user.objectId, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Session-Token': user.sessionToken, 'X-Parse-REST-API-Key': 'rest', }, json: { authData: {anonymous: null}, username: 'user', email: 'user@email.com', password: 'password', } }); }).then(() => { expect(emailCalled).toBe(true); expect(emailOptions).not.toBeUndefined(); expect(emailOptions.user.get('email')).toEqual('user@email.com'); done(); }).catch((err) => { jfail(err); fail('no request should fail: ' + JSON.stringify(err)); done(); }); }); it('should not send email when email is not a string', (done) => { let emailCalled = false; let emailOptions; var emailAdapter = { sendVerificationEmail: (options) => { emailOptions = options; emailCalled = true; }, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve() } reconfigureServer({ appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, publicServerURL: 'http://localhost:8378/1', }); var user = new Parse.User(); user.set('username', 'asdf@jkl.com'); user.set('password', 'zxcv'); user.set('email', 'asdf@jkl.com'); user.signUp(null, { success: (user) => { return rp.post({ url: 'http://localhost:8378/1/requestPasswordReset', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Session-Token': user.sessionToken, 'X-Parse-REST-API-Key': 'rest', }, json: { email: {"$regex":"^asd"}, } }).then((res) => { fail('no request should succeed: ' + JSON.stringify(res)); done(); }).catch((err) => { expect(emailCalled).toBeTruthy(); expect(emailOptions).toBeDefined(); expect(err.statusCode).toBe(400); expect(err.message).toMatch('{"code":125,"error":"you must provide a valid email string"}'); done(); }); }, }); }); it('should aftersave with full object', (done) => { var hit = 0; Parse.Cloud.afterSave('_User', (req, res) => { hit++; expect(req.object.get('username')).toEqual('User'); res.success(); }); const user = new Parse.User() user.setUsername('User'); user.setPassword('pass'); user.signUp().then(()=> { user.set('hello', 'world'); return user.save(); }).then(() => { expect(hit).toBe(2); done(); }); }); it('changes to a user should update the cache', (done) => { Parse.Cloud.define('testUpdatedUser', (req, res) => { expect(req.user.get('han')).toEqual('solo'); res.success({}); }); const user = new Parse.User(); user.setUsername('harrison'); user.setPassword('ford'); user.signUp().then(() => { user.set('han', 'solo'); return user.save(); }).then(() => { return Parse.Cloud.run('testUpdatedUser'); }).then(() => { done(); }, () => { fail('Should not have failed.'); done(); }); }); it('should fail to become user with expired token', (done) => { let token; Parse.User.signUp("auser", "somepass", null) .then(() => rp({ method: 'GET', url: 'http://localhost:8378/1/classes/_Session', json: true, headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Master-Key': 'test', }, })) .then(body => { var id = body.results[0].objectId; var expiresAt = new Date((new Date()).setYear(2015)); token = body.results[0].sessionToken; return rp({ method: 'PUT', url: "http://localhost:8378/1/classes/_Session/" + id, json: true, headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-Master-Key': 'test', }, body: { expiresAt: { __type: "Date", iso: expiresAt.toISOString() }, }, }) }) .then(() => Parse.User.become(token)) .then(() => { fail("Should not have succeded") done(); }, error => { expect(error.code).toEqual(209); expect(error.message).toEqual("Session token is expired."); done(); }) }); it('should not create extraneous session tokens', (done) => { const config = Config.get(Parse.applicationId); config.database.loadSchema().then((s) => { // Lock down the _User class for creation return s.addClassIfNotExists('_User', {}, {create: {}}) }).then(() => { const user = new Parse.User(); return user.save({'username': 'user', 'password': 'pass'}); }).then(() => { fail('should not be able to save the user'); }, () => { return Promise.resolve(); }).then(() => { const q = new Parse.Query('_Session'); return q.find({useMasterKey: true}) }).then((res) => { // We should have no session created expect(res.length).toBe(0); done(); }, () => { fail('should not fail'); done(); }); }); it('should not overwrite username when unlinking facebook user (regression test for #1532)', done => { Parse.Object.disableSingleInstance(); var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); var user = new Parse.User(); user.set("username", "testLinkWithProvider"); user.set("password", "mypass"); user.signUp() .then(user => user._linkWith("facebook", { success: user => { expect(user.get('username')).toEqual('testLinkWithProvider'); expect(Parse.FacebookUtils.isLinked(user)).toBeTruthy(); return user._unlinkFrom('facebook') .then(() => user.fetch()) .then(user => { expect(user.get('username')).toEqual('testLinkWithProvider'); expect(Parse.FacebookUtils.isLinked(user)).toBeFalsy(); done(); }); }, error: error => { fail('Unexpected failure testing linking'); fail(JSON.stringify(error)); done(); } })) .catch(error => { fail('Unexpected failure testing in unlink user test'); jfail(error); done(); }); }); it('should revoke sessions when converting anonymous user to "normal" user', done => { request.post({ url: 'http://localhost:8378/1/classes/_User', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: {authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}} }, (err, res, body) => { Parse.User.become(body.sessionToken) .then(user => { const obj = new Parse.Object('TestObject'); obj.setACL(new Parse.ACL(user)); return obj.save() .then(() => { // Change password, revoking session user.set('username', 'no longer anonymous'); user.set('password', 'password'); return user.save() }) .then(() => { // Session token should have been recycled expect(body.sessionToken).not.toEqual(user.getSessionToken()); }) .then(() => obj.fetch()) .then(() => { done(); }) .catch(() => { fail('should not fail') done(); }); }) }); }); it('should not revoke session tokens if the server is configures to not revoke session tokens', done => { reconfigureServer({ revokeSessionOnPasswordReset: false }) .then(() => { request.post({ url: 'http://localhost:8378/1/classes/_User', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: {authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}} }, (err, res, body) => { Parse.User.become(body.sessionToken) .then(user => { const obj = new Parse.Object('TestObject'); obj.setACL(new Parse.ACL(user)); return obj.save() .then(() => { // Change password, revoking session user.set('username', 'no longer anonymous'); user.set('password', 'password'); return user.save() }) .then(() => obj.fetch()) // fetch should succeed as we still have our session token .then(done, fail); }) }); }); }); it('should not fail querying non existing relations', done => { const user = new Parse.User(); user.set({ username: 'hello', password: 'world' }) user.signUp().then(() => { return Parse.User.current().relation('relation').query().find(); }).then((res) => { expect(res.length).toBe(0); done(); }).catch((err) => { fail(JSON.stringify(err)); done(); }); }); it('should not allow updates to emailVerified', done => { var emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve() } const user = new Parse.User(); user.set({ username: 'hello', password: 'world', email: "test@email.com" }) reconfigureServer({ appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, publicServerURL: "http://localhost:8378/1" }).then(() => { return user.signUp(); }).then(() => { return Parse.User.current().set('emailVerified', true).save(); }).then(() => { fail("Should not be able to update emailVerified"); done(); }).catch((err) => { expect(err.message).toBe("Clients aren't allowed to manually update email verification."); done(); }); }); it('should not retrieve hidden fields', done => { var emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve() } const user = new Parse.User(); user.set({ username: 'hello', password: 'world', email: "test@email.com" }) reconfigureServer({ appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, publicServerURL: "http://localhost:8378/1" }).then(() => { return user.signUp(); }).then(() => rp({ method: 'GET', url: 'http://localhost:8378/1/users/me', json: true, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Session-Token': Parse.User.current().getSessionToken(), 'X-Parse-REST-API-Key': 'rest' }, })).then((res) => { expect(res.emailVerified).toBe(false); expect(res._email_verify_token).toBeUndefined(); done() }).then(() => rp({ method: 'GET', url: 'http://localhost:8378/1/users/' + Parse.User.current().id, json: true, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, })).then((res) => { expect(res.emailVerified).toBe(false); expect(res._email_verify_token).toBeUndefined(); done() }).catch((err) => { fail(JSON.stringify(err)); done(); }); }); it('should not allow updates to hidden fields', done => { var emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve() } const user = new Parse.User(); user.set({ username: 'hello', password: 'world', email: "test@email.com" }) reconfigureServer({ appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, publicServerURL: "http://localhost:8378/1" }).then(() => { return user.signUp(); }).then(() => { return Parse.User.current().set('_email_verify_token', 'bad').save(); }).then(() => { fail("Should not be able to update email verification token"); done(); }).catch((err) => { expect(err).toBeDefined(); done(); }); }); it('should revoke sessions when setting paswword with masterKey (#3289)', (done) => { let user; Parse.User.signUp('username', 'password') .then((newUser) => { user = newUser; user.set('password', 'newPassword'); return user.save(null, {useMasterKey: true}); }).then(() => { const query = new Parse.Query('_Session'); query.equalTo('user', user); return query.find({useMasterKey: true}); }).then((results) => { expect(results.length).toBe(0); done(); }, done.fail); }); it('should not send a verification email if the user signed up using oauth', (done) => { let emailCalledCount = 0; const emailAdapter = { sendVerificationEmail: () => { emailCalledCount++; return Promise.resolve(); }, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve() } reconfigureServer({ appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, publicServerURL: "http://localhost:8378/1" }); const user = new Parse.User(); user.set('email', 'email1@host.com'); Parse.FacebookUtils.link(user, { id: "8675309", access_token: "jenny", expiration_date: new Date().toJSON() }).then((user) => { user.set('email', 'email2@host.com'); user.save().then(() => { expect(emailCalledCount).toBe(0); done(); }); }); }); it('should be able to update user with authData passed', (done) => { let objectId; let sessionToken; function validate(block) { return rp.get({ url: `http://localhost:8378/1/classes/_User/${objectId}`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', 'X-Parse-Session-Token': sessionToken }, json: true, }).then(block); } rp.post({ url: 'http://localhost:8378/1/classes/_User', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: { key: "value", authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}} }).then((body) => { objectId = body.objectId; sessionToken = body.sessionToken; expect(sessionToken).toBeDefined(); expect(objectId).toBeDefined(); return validate((user) => { // validate that keys are set on creation expect(user.key).toBe("value"); }); }).then(() => { // update the user const options = { url: `http://localhost:8378/1/classes/_User/${objectId}`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', 'X-Parse-Session-Token': sessionToken }, json: { key: "otherValue", authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}} } return rp.put(options); }).then(() => { return validate((user) => { // validate that keys are set on update expect(user.key).toBe("otherValue"); }); }).then(() => { done(); }) .then(done) .catch(done.fail); }); it('can login with email', (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: { email: "yo@lo.com", password: 'yolopass'} } return rp.get(options); }).then(done).catch(done.fail); }); it('cannot login with email and invalid password', (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: { email: "yo@lo.com", password: 'yolopass2'} } return rp.get(options); }).then(done.fail).catch(done); }); it('can login with email through query string', (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&password=yolopass`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, } return rp.get(options); }).then(done).catch(done.fail); }); it('can login when both email and username are passed', (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo&password=yolopass`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, } return rp.get(options); }).then(done).catch(done.fail); }); it("fails to login when username doesn't match email", (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo2&password=yolopass`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: true, } return rp.get(options); }).then(done.fail).catch((err) => { expect(err.response.body.error).toEqual('Invalid username/password.'); done(); }); }); it("fails to login when email doesn't match username", (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo2.com&username=yolo&password=yolopass`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: true, } return rp.get(options); }).then(done.fail).catch((err) => { expect(err.response.body.error).toEqual('Invalid username/password.'); done(); }); }); it('fails to login when email and username are not provided', (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login?password=yolopass`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: true, } return rp.get(options); }).then(done.fail).catch((err) => { expect(err.response.body.error).toEqual('username/email is required.'); done(); }); }); it('fails to login when password is not provided', (done) => { const user = new Parse.User(); user.save({ username: 'yolo', password: 'yolopass', email: 'yo@lo.com' }).then(() => { const options = { url: `http://localhost:8378/1/login?username=yolo`, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest', }, json: true, } return rp.get(options); }).then(done.fail).catch((err) => { expect(err.response.body.error).toEqual('password is required.'); done(); }); }); });