mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
Merge pull request #1242 from PR0M3TH3AN/codex/add-access-control-check-in-login
Enforce access-control check in AuthService.login
This commit is contained in:
2 files changed
+139
-6
No files matched your search
@@ -500,6 +500,35 @@ export default class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
if (control && typeof control.canAccess === "function") {
|
||||
let canAccess = true;
|
||||
try {
|
||||
canAccess = control.canAccess(candidateNpub || nextPubkey);
|
||||
} catch (error) {
|
||||
this.log("[AuthService] accessControl.canAccess threw", error);
|
||||
canAccess = Boolean(canAccess);
|
||||
}
|
||||
|
||||
if (!canAccess) {
|
||||
let isBlocked = false;
|
||||
if (typeof control.isBlacklisted === "function") {
|
||||
try {
|
||||
isBlocked = control.isBlacklisted(candidateNpub || nextPubkey);
|
||||
} catch (error) {
|
||||
this.log("[AuthService] accessControl.isBlacklisted threw", error);
|
||||
isBlocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
const accessError = new Error(
|
||||
isBlocked
|
||||
? "Your account has been blocked on this platform."
|
||||
: "Access restricted to admins and moderators users only.",
|
||||
);
|
||||
throw accessError;
|
||||
}
|
||||
}
|
||||
|
||||
if (normalized) {
|
||||
setPubkey(normalized);
|
||||
if (this.nostrClient && typeof this.nostrClient === "object") {
|
||||
|
||||
@@ -287,6 +287,11 @@ export default class LoginModalController {
|
||||
this.nextRequestLoginOptionsResolver = null;
|
||||
this.pendingTask = null;
|
||||
this.modalPrepared = false;
|
||||
// Track generated keys so we only create one keypair per modal session.
|
||||
this.generatedKeypair = null;
|
||||
// Track modal close state to reset generated keys when the modal closes.
|
||||
this.modalCloseObserver = null;
|
||||
this.modalCloseIntervalId = null;
|
||||
|
||||
this.initializeRemoteSignerStatus();
|
||||
this.initialized = false;
|
||||
@@ -310,6 +315,8 @@ export default class LoginModalController {
|
||||
this.template = this.resolveTemplate();
|
||||
|
||||
this.renderProviders();
|
||||
// Start tracking modal close events to reset per-session key generation state.
|
||||
this.initializeModalCloseTracking();
|
||||
|
||||
if (typeof this.lastRemoteSignerStatus !== "undefined") {
|
||||
this.applyRemoteSignerStatus(this.lastRemoteSignerStatus);
|
||||
@@ -319,6 +326,57 @@ export default class LoginModalController {
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
initializeModalCloseTracking() {
|
||||
const modal = this.modalElement;
|
||||
if (!(modal instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
if (!this.isModalOpen()) {
|
||||
// Reset generated keys once the modal is closed to allow a new keypair later.
|
||||
this.resetGeneratedKeypair();
|
||||
}
|
||||
};
|
||||
|
||||
// Prefer a MutationObserver, fall back to polling if needed.
|
||||
if (typeof MutationObserver === "function") {
|
||||
if (this.modalCloseObserver) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const observer = new MutationObserver(handleClose);
|
||||
observer.observe(modal, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-open", "class"],
|
||||
});
|
||||
this.modalCloseObserver = observer;
|
||||
return;
|
||||
} catch (error) {
|
||||
devLogger.warn(
|
||||
"[LoginModalController] Failed to observe login modal close state:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.modalCloseIntervalId ||
|
||||
!this.window ||
|
||||
typeof this.window.setInterval !== "function"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Poll for modal close state as a fallback when observers are unavailable.
|
||||
this.modalCloseIntervalId = this.window.setInterval(handleClose, 500);
|
||||
}
|
||||
|
||||
resetGeneratedKeypair() {
|
||||
// Clear cached keypair so the next modal session generates fresh keys.
|
||||
this.generatedKeypair = null;
|
||||
}
|
||||
|
||||
ensureModalPrepared() {
|
||||
if (!(this.modalElement instanceof HTMLElement)) {
|
||||
return null;
|
||||
@@ -339,6 +397,8 @@ export default class LoginModalController {
|
||||
}
|
||||
|
||||
this.modalPrepared = true;
|
||||
// Ensure close tracking starts after modal preparation updates the element.
|
||||
this.initializeModalCloseTracking();
|
||||
return this.modalElement instanceof HTMLElement ? this.modalElement : null;
|
||||
}
|
||||
|
||||
@@ -1636,12 +1696,26 @@ export default class LoginModalController {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
const sk = tools.generateSecretKey();
|
||||
const pk = tools.getPublicKey(sk);
|
||||
const npub = tools.nip19?.npubEncode ? tools.nip19.npubEncode(pk) : pk;
|
||||
const nsec = tools.nip19?.nsecEncode ? tools.nip19.nsecEncode(sk) : "";
|
||||
const hexSk = nsec ? nsec : tools.nip19?.bytesToHex ? tools.nip19.bytesToHex(sk) : ""; // Fallback if nsecEncode missing? unlikely with recent tools
|
||||
// Generate keys only once per modal session to avoid duplicates.
|
||||
if (!this.generatedKeypair) {
|
||||
const sk = tools.generateSecretKey();
|
||||
const pk = tools.getPublicKey(sk);
|
||||
const npub = tools.nip19?.npubEncode ? tools.nip19.npubEncode(pk) : pk;
|
||||
const nsec = tools.nip19?.nsecEncode ? tools.nip19.nsecEncode(sk) : "";
|
||||
const hexSk = nsec
|
||||
? nsec
|
||||
: tools.nip19?.bytesToHex
|
||||
? tools.nip19.bytesToHex(sk)
|
||||
: "";
|
||||
this.generatedKeypair = {
|
||||
npub,
|
||||
nsec,
|
||||
hexSk,
|
||||
pubkey: pk,
|
||||
};
|
||||
}
|
||||
|
||||
const { npub, nsec, hexSk } = this.generatedKeypair;
|
||||
|
||||
// Display keys
|
||||
if (npubEl) npubEl.textContent = npub;
|
||||
@@ -1695,6 +1769,36 @@ export default class LoginModalController {
|
||||
|
||||
if (loginBtn) {
|
||||
loginBtn.addEventListener("click", () => {
|
||||
// Block generated accounts if whitelist-only access is enforced.
|
||||
const accessControl = this.services?.authService?.accessControl;
|
||||
if (accessControl && typeof accessControl.canAccess === "function") {
|
||||
let canAccess = true;
|
||||
try {
|
||||
canAccess = accessControl.canAccess(npub);
|
||||
} catch (error) {
|
||||
devLogger.warn(
|
||||
"[LoginModalController] accessControl.canAccess threw:",
|
||||
error,
|
||||
);
|
||||
canAccess = Boolean(canAccess);
|
||||
}
|
||||
|
||||
if (!canAccess && typeof accessControl.whitelistMode === "function") {
|
||||
const isWhitelistMode = accessControl.whitelistMode();
|
||||
if (isWhitelistMode) {
|
||||
// Surface the access error via the login error callback/notification.
|
||||
const accessError = new Error(
|
||||
"Access restricted to admins and moderators users only.",
|
||||
);
|
||||
safeInvoke(this.callbacks.onLoginError, {
|
||||
message: accessError.message,
|
||||
error: accessError,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return credentials compatible with nsec provider
|
||||
finish({ secret: nsec || hexSk, persist: false });
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user