Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4096761
secureEnclave.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
secureEnclave.js
View Options
const
ProgressIndicatorWidget
=
require
(
'./ProgressIndicatorWidget.js'
);
const
ErrorWidget
=
require
(
'./ErrorWidget.js'
);
const
wiki
=
mw
.
config
.
get
(
'wgDBname'
);
const
{
loadHCaptcha
,
executeHCaptcha
,
mapErrorCodeToMessageKey
}
=
require
(
'./utils.js'
);
/**
* Load hCaptcha in Secure Enclave mode.
*
* @param {jQuery} $form The form to be protected by hCaptcha.
* @param {jQuery} $hCaptchaField The hCaptcha input field within the form.
* @param {Window} win
* @param {string} interfaceName The name of the interface where hCaptcha is being used
* @return {Promise<void>} A promise that resolves if hCaptcha failed to initialize,
* or after the first time the user attempts to submit the form and hCaptcha finishes running.
*/
async
function
setupHCaptcha
(
$form
,
$hCaptchaField
,
win
,
interfaceName
)
{
const
loadingIndicator
=
new
ProgressIndicatorWidget
(
mw
.
msg
(
'hcaptcha-loading-indicator-label'
)
);
loadingIndicator
.
$element
.
addClass
(
'ext-confirmEdit-hCaptchaLoadingIndicator'
);
loadingIndicator
.
$element
.
hide
();
const
errorWidget
=
new
ErrorWidget
();
$hCaptchaField
.
after
(
loadingIndicator
.
$element
,
errorWidget
.
$element
);
const
hCaptchaLoaded
=
loadHCaptcha
(
win
,
interfaceName
);
// Errors that can be recovered from by restarting the workflow.
const
recoverableErrors
=
[
'challenge-closed'
,
'challenge-expired'
];
/**
* Fires when a visible challenge is displayed.
*/
const
onOpen
=
function
()
{
mw
.
track
(
'stats.mediawiki_confirmedit_hcaptcha_open_callback_total'
,
1
,
{
wiki
:
wiki
}
);
// Fire an event that can be used in WikimediaEvents for associating
// challenge opens with a user.
mw
.
track
(
'confirmEdit.hCaptchaRenderCallback'
,
'open'
,
interfaceName
);
};
const
captchaIdPromise
=
hCaptchaLoaded
.
then
(
()
=>
win
.
hcaptcha
.
render
(
'h-captcha'
,
{
'open-callback'
:
onOpen
,
'close-callback'
:
()
=>
{
mw
.
track
(
'confirmEdit.hCaptchaRenderCallback'
,
'close'
,
interfaceName
);
},
'chalexpired-callback'
:
()
=>
{
mw
.
track
(
'confirmEdit.hCaptchaRenderCallback'
,
'chalexpired'
,
interfaceName
);
},
'expired-callback'
:
()
=>
{
mw
.
track
(
'confirmEdit.hCaptchaRenderCallback'
,
'expired'
,
interfaceName
);
},
'error-callback'
:
()
=>
{
mw
.
track
(
'confirmEdit.hCaptchaRenderCallback'
,
'error'
,
interfaceName
);
}
}
)
);
/**
* Trigger a single hCaptcha workflow execution.
*
* @return {Promise<void>} A promise that resolves if hCaptcha failed to initialize,
* or after the first time the user attempts to submit the form and hCaptcha finishes running.
*/
const
executeWorkflow
=
async
function
()
{
$form
.
off
(
'submit.hCaptcha'
);
const
formSubmitted
=
new
Promise
(
(
resolve
)
=>
{
$form
.
on
(
'submit.hCaptcha'
,
function
(
event
)
{
event
.
preventDefault
();
resolve
(
this
);
}
);
}
);
/**
* Displays an error returned by attempting to load or execute hCaptcha
* in a user-friendly way
*
* @param {string} error The error as returned by `executeHCaptcha` or `loadHCaptcha`
*/
const
displayErrorInErrorWidget
=
(
error
)
=>
{
// Possible message keys used here:
// * hcaptcha-generic-error
// * hcaptcha-challenge-closed
// * hcaptcha-challenge-expired
errorWidget
.
show
(
mw
.
msg
(
mapErrorCodeToMessageKey
(
error
)
)
);
};
return
Promise
.
all
(
[
captchaIdPromise
,
formSubmitted
]
)
.
then
(
(
[
captchaId
,
form
]
)
=>
{
loadingIndicator
.
$element
.
show
();
return
executeHCaptcha
(
win
,
captchaId
,
interfaceName
)
.
then
(
(
response
)
=>
{
// Clear out any errors from a previous workflow.
errorWidget
.
hide
();
// Set the hCaptcha response input field, which does not yet exist
$form
.
append
(
$
(
'<input>'
)
.
attr
(
'type'
,
'hidden'
)
.
attr
(
'name'
,
'h-captcha-response'
)
.
attr
(
'id'
,
'h-captcha-response'
)
.
val
(
response
)
);
// Hide the loading indicator as we have finished hCaptcha
// and are submitting the form
loadingIndicator
.
$element
.
hide
();
mw
.
hook
(
'confirmEdit.hCaptcha.executionSuccess'
).
fire
(
response
);
form
.
submit
();
}
)
.
catch
(
(
error
)
=>
{
loadingIndicator
.
$element
.
hide
();
displayErrorInErrorWidget
(
error
);
// Initiate a new workflow for recoverable errors
// (e.g. an expired or closed challenge).
if
(
recoverableErrors
.
includes
(
error
)
)
{
return
executeWorkflow
();
}
}
);
}
)
.
catch
(
(
error
)
=>
{
displayErrorInErrorWidget
(
error
);
}
);
};
return
executeWorkflow
();
}
/**
* Configure hCaptcha in Secure Enclave mode.
*
* @param {Window} win
* @return {Promise<void>} A promise that resolves if hCaptcha failed to initialize,
* or after the first time the user attempts to submit the form and hCaptcha finishes running.
*/
async
function
useSecureEnclave
(
win
)
{
// eslint-disable-next-line no-jquery/no-global-selector
const
$hCaptchaField
=
$
(
'#h-captcha'
);
if
(
!
$hCaptchaField
.
length
)
{
return
;
}
const
$form
=
$hCaptchaField
.
closest
(
'form'
);
if
(
!
$form
.
length
)
{
return
;
}
// Work our what interface we are loading hCaptcha on, currently only used
// for instrumentation purposes
let
interfaceName
=
'unknown'
;
if
(
mw
.
config
.
get
(
'wgCanonicalSpecialPageName'
)
===
'CreateAccount'
)
{
interfaceName
=
'createaccount'
;
}
if
(
mw
.
config
.
get
(
'wgAction'
)
===
'edit'
)
{
interfaceName
=
'edit'
;
}
// Load hCaptcha the first time the user interacts with the form.
return
new
Promise
(
(
resolve
)
=>
{
const
$inputs
=
$form
.
find
(
'input, textarea'
);
// Catch and prevent form submissions that occur before hCaptcha was initialized.
$form
.
one
(
'submit.hCaptchaLoader'
,
(
event
)
=>
{
event
.
preventDefault
();
$inputs
.
off
(
'input.hCaptchaLoader focus.hCaptchaLoader'
);
$form
.
off
(
'submit.hCaptchaLoader'
);
resolve
(
setupHCaptcha
(
$form
,
$hCaptchaField
,
win
,
interfaceName
)
);
}
);
$inputs
.
one
(
'input.hCaptchaLoader focus.hCaptchaLoader'
,
()
=>
{
$inputs
.
off
(
'input.hCaptchaLoader focus.hCaptchaLoader'
);
$form
.
off
(
'submit.hCaptchaLoader'
);
resolve
(
setupHCaptcha
(
$form
,
$hCaptchaField
,
win
,
interfaceName
)
);
}
);
}
);
}
module
.
exports
=
useSecureEnclave
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Aug 18, 14:29 (3 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
2e/da/7e2d0f1a33689cac9dc3e2218381
Default Alt Text
secureEnclave.js (6 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment