fix: Endpoints /login and /verifyPassword ignore _User protectedFields (#10409)

This commit is contained in:
Manuel
2026-04-07 13:01:11 +01:00
committed by GitHub
parent 57a4fba827
commit 8a3db3b966
2 changed files with 116 additions and 5 deletions
+58
View File
@@ -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: {
+58 -5
View File
@@ -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;