diff --git a/spec/ProtectedFields.spec.js b/spec/ProtectedFields.spec.js index ba8ce1e8eb..abaac18c2c 100644 --- a/spec/ProtectedFields.spec.js +++ b/spec/ProtectedFields.spec.js @@ -1973,6 +1973,64 @@ describe('ProtectedFields', function () { expect(response.data.objectId).toBe(user.id); }); + it('/login respects protectedFieldsOwnerExempt: false', async function () { + await reconfigureServer({ + protectedFields: { + _User: { + '*': ['phone'], + }, + }, + protectedFieldsOwnerExempt: false, + }); + const user = await Parse.User.signUp('user1', 'password'); + const sessionToken = user.getSessionToken(); + user.set('phone', '555-1234'); + await user.save(null, { sessionToken }); + + const response = await request({ + method: 'POST', + url: 'http://localhost:8378/1/login', + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username: 'user1', password: 'password' }), + }); + expect(response.data.phone).toBeUndefined(); + expect(response.data.objectId).toBe(user.id); + expect(response.data.sessionToken).toBeDefined(); + }); + + it('/verifyPassword respects protectedFieldsOwnerExempt: false', async function () { + await reconfigureServer({ + protectedFields: { + _User: { + '*': ['phone'], + }, + }, + protectedFieldsOwnerExempt: false, + verifyUserEmails: false, + }); + const user = await Parse.User.signUp('user1', 'password'); + const sessionToken = user.getSessionToken(); + user.set('phone', '555-1234'); + await user.save(null, { sessionToken }); + + const response = await request({ + method: 'POST', + url: 'http://localhost:8378/1/verifyPassword', + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username: 'user1', password: 'password' }), + }); + expect(response.data.phone).toBeUndefined(); + expect(response.data.objectId).toBe(user.id); + }); + it('owner sees non-protected fields like email when protectedFieldsOwnerExempt is true', async function () { await reconfigureServer({ protectedFields: { diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 01e62e01a0..5b0eebe608 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -364,12 +364,39 @@ export class UsersRouter extends ClassesRouter { req.info.context ); + // Re-fetch the user with the caller's auth context so that + // protectedFields and CLP apply correctly + const userAuth = new Auth.Auth({ + config: req.config, + isMaster: false, + user: Parse.Object.fromJSON({ className: '_User', objectId: user.objectId }), + installationId: req.info.installationId, + }); + let filteredUser; + try { + const filteredUserResponse = await rest.get( + req.config, + userAuth, + '_User', + user.objectId, + {}, + req.info.clientSDK, + req.info.context + ); + filteredUser = filteredUserResponse.results?.[0]; + } catch { + // re-fetch may fail for legacy users without ACL; fall through + } + if (!filteredUser) { + filteredUser = user; + } + UsersRouter.removeHiddenProperties(filteredUser); + filteredUser.sessionToken = user.sessionToken; if (authDataResponse) { - user.authDataResponse = authDataResponse; + filteredUser.authDataResponse = authDataResponse; } - await req.config.authDataManager.runAfterFind(req, user.authData); - return { response: user }; + return { response: filteredUser }; } /** @@ -436,8 +463,34 @@ export class UsersRouter extends ClassesRouter { .then(async user => { // Remove hidden properties. UsersRouter.removeHiddenProperties(user); - await req.config.authDataManager.runAfterFind(req, user.authData); - return { response: user }; + // Re-fetch the user with the caller's auth context so that + // protectedFields and CLP apply correctly + const userAuth = new Auth.Auth({ + config: req.config, + isMaster: false, + user: Parse.Object.fromJSON({ className: '_User', objectId: user.objectId }), + installationId: req.info.installationId, + }); + let filteredUser; + try { + const filteredUserResponse = await rest.get( + req.config, + userAuth, + '_User', + user.objectId, + {}, + req.info.clientSDK, + req.info.context + ); + filteredUser = filteredUserResponse.results?.[0]; + } catch { + // re-fetch may fail for legacy users without ACL; fall through + } + if (!filteredUser) { + filteredUser = user; + } + UsersRouter.removeHiddenProperties(filteredUser); + return { response: filteredUser }; }) .catch(error => { throw error;