re-check who the current user is instead of trusting the first answer
The result of Users/Me was cached in a module level promise for the lifetime of the page. The web client is a single page app, so signing out or switching accounts never reloads the document and the script kept whichever user it saw first. Open the page as an admin, switch to a normal user in the same tab, and isAdministrator() still said yes: the admin-only ShareLink action was injected into that user's menus. The cached user and guest state are now keyed to ApiClient.getCurrentUserId(), so a switch invalidates them, and a non-admin verdict actively removes any action left in the DOM by the previous session instead of just declining to add one. Nothing was reachable through this. The create endpoint checks the Administrator role server side and answers 403 for a non-admin (confirmed against a real non-admin session), so the button was there but did nothing.
Cette révision appartient à :
@@ -2,14 +2,16 @@
|
|||||||
var pluginId = '68540b76-ee74-436d-85ff-2abc884bbea6';
|
var pluginId = '68540b76-ee74-436d-85ff-2abc884bbea6';
|
||||||
var copyLabel = 'Copy Stream URL';
|
var copyLabel = 'Copy Stream URL';
|
||||||
var actionLabel = 'ShareLink';
|
var actionLabel = 'ShareLink';
|
||||||
var clientVersion = '1.0.2-ui-1';
|
var clientVersion = '1.0.2-ui-3';
|
||||||
var allowedItemStorageKey = 'sharelinks.allowedItemId';
|
var allowedItemStorageKey = 'sharelinks.allowedItemId';
|
||||||
var guestClassName = 'sharelinks-guest';
|
var guestClassName = 'sharelinks-guest';
|
||||||
var hiddenAttr = 'data-sharelinks-hidden';
|
var hiddenAttr = 'data-sharelinks-hidden';
|
||||||
var injectedAttr = 'data-sharelinks-injected';
|
var injectedAttr = 'data-sharelinks-injected';
|
||||||
var configPromise = null;
|
var configPromise = null;
|
||||||
var userPromise = null;
|
var userPromise = null;
|
||||||
|
var userPromiseUserId = null;
|
||||||
var guestStatePromise = null;
|
var guestStatePromise = null;
|
||||||
|
var guestStatePromiseUserId = null;
|
||||||
var booted = false;
|
var booted = false;
|
||||||
var scanQueued = false;
|
var scanQueued = false;
|
||||||
var bootRetry = null;
|
var bootRetry = null;
|
||||||
@@ -222,21 +224,41 @@
|
|||||||
return configPromise;
|
return configPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The web client is a single page app: signing out or switching accounts does
|
||||||
|
* not reload the document, so anything cached for "the current user" has to be
|
||||||
|
* keyed to the session it came from. Caching it for the lifetime of the page
|
||||||
|
* let an admin's verdict survive into the next user's session.
|
||||||
|
*/
|
||||||
function getCurrentUser() {
|
function getCurrentUser() {
|
||||||
if (!userPromise) {
|
var userId = currentApiUserId();
|
||||||
|
if (!userPromise || userPromiseUserId !== userId) {
|
||||||
|
userPromiseUserId = userId;
|
||||||
userPromise = apiGet('Users/Me').catch(function () {
|
userPromise = apiGet('Users/Me').catch(function () {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return userPromise;
|
return userPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function currentApiUserId() {
|
||||||
|
try {
|
||||||
|
return (window.ApiClient && ApiClient.getCurrentUserId && ApiClient.getCurrentUserId()) || '';
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getGuestState() {
|
function getGuestState() {
|
||||||
if (!guestStatePromise) {
|
var userId = currentApiUserId();
|
||||||
|
if (!guestStatePromise || guestStatePromiseUserId !== userId) {
|
||||||
|
guestStatePromiseUserId = userId;
|
||||||
guestStatePromise = apiGet('ShareLinks/GuestState').catch(function () {
|
guestStatePromise = apiGet('ShareLinks/GuestState').catch(function () {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return guestStatePromise;
|
return guestStatePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +458,9 @@
|
|||||||
async function scanForMoreMenuActions() {
|
async function scanForMoreMenuActions() {
|
||||||
var user = await getCurrentUser();
|
var user = await getCurrentUser();
|
||||||
if (!isAdministrator(user)) {
|
if (!isAdministrator(user)) {
|
||||||
|
// Take back anything injected for a previous session rather than only
|
||||||
|
// skipping: a switch inside the SPA leaves the old DOM in place.
|
||||||
|
removeInjectedActions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,6 +480,12 @@
|
|||||||
appendActionSection(container, itemId);
|
appendActionSection(container, itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeInjectedActions() {
|
||||||
|
Array.prototype.forEach.call(document.querySelectorAll('[' + injectedAttr + '="1"]'), function (node) {
|
||||||
|
node.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records which item's "more" menu is about to open. The action sheet is a
|
* Records which item's "more" menu is about to open. The action sheet is a
|
||||||
* detached, body-level element with no link back to the card or row it was
|
* detached, body-level element with no link back to the card or row it was
|
||||||
|
|||||||
Référencer dans un nouveau ticket
Bloquer un utilisateur