Skip to content

Commit

Permalink
Merge pull request #13 from openpmix/hcaptcha-test2
Browse files Browse the repository at this point in the history
Hcaptcha test2
  • Loading branch information
naughtont3 authored Jan 11, 2025
2 parents 27c7ef5 + 36c15cf commit ea23871
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 6 deletions.
87 changes: 87 additions & 0 deletions captcha/_misc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
README
------

The meeting links are slightly hidden to avoid abuse from web scraping bots,
which is why we use the hCAPTCHA to add a small deterrent.

If just changing the meeting info/links, skip down to `Updating Meeting
Info` section.


Background
----------
The github pages are statically generated. Therefore, we can not use a
server-side reCAPTCHA method (e.g., Open MPI uses a server-side PHP
reCAPTCHA). We use hCAPTCHA and an entirely client-side method that is
compatible with static gh-pages.

Note, since the data is pasted in the client side, we base64 encode the
information to obfuscate the information and reduce direct browsing of
meeting info. See notes for updating things and encoding the meeting info.

General Flow
------------

1. User visits `/captcha` URL, and completes the challenge prompt.

2. On success, redirected to "success" page (e.g., `success.html`) with
the meeting information.
- Note: We generate a simple token on captcha page (`index.html`) that
is checked by the `success.html` page to try and avoid simple browsing
of the full URL. This is a weak protection to help obfuscate the
meeting info data.
- Note: We base64 encode the meeting content and embed the data into the
success page where it is displayed. This is a weak protection to help
obfuscate the meeting info data.

3. On failure, redirected to captcha challenge prompt.


Initial Setup
-------------

One-time setup for the basic `/captcha` directory in repository.

1. (One-time setup) Create hCaptcha free account and generate `sitekey`
- Note: Currently using setup under naughtont AT ornl.gov account.
- Note: Using 'Always Challenge' and 'Easy' settings.
- https://dashboard.hcaptcha.com/sites?page=1&archived=active

2. Edit `index.html`, update `data-sitekey` with `sitekey` from Step 1.

3. Commit changes to repo and the gh-pages will be updated automatically.

4. Now ready to update meeting info (see below).


Update Meeting Info
-------------------

Assumes initial setup (above) complete and just changing meeting data.

1. Manually format the "meeting info" with any desired HTML formatting.

2. Encode (base64) the meeting data (e.g., `meetings.dat => meetings.dat.enc`)
- NOTE: You can decode the meeting data to double check encode/decode
using the same script (see: `encode.py -h`)

```
./_misc/encode.py meetings.dat > meetings.dat.enc
```
3. Edit `success.html`, update `encodedData` with contents of encoded data
string (e.g., copy/paste `meetings.dat.enc`).
4. As needed, update any titles/headings in HTML (e.g., 2025 Meetings).
5. Commit changes to repo and the gh-pages will be updated automatically.
Misc
----
- The `_misc/` dir has leading underscore so Jekyll ignores it
when posting content to the statically generated gh-pages
- Encode script at `_misc/encode.py`
- Example HTML formatted meeting data at `_misc/example.dat`
62 changes: 62 additions & 0 deletions captcha/_misc/encode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
# Script to base64 encode the contents of a file.
# The script can also decode the encoded data if needed.
#
# NOTE: The base64 encoded data is typically the contents of
# the meeting links so that the file committed to the repository
# and posted online is obfuscated to avoid online browsing.
#
import base64
import sys
import argparse

decode=0
filename=""
script_name = "encode.py"

def read_file(file_path):
content = ""
try:
with open(file_path, 'r') as file:
content = file.read()
except Exception as e:
print(f"Error: Unexpected error occurred: {e}", file=sys.stderr)

return(content)

def main():
p = argparse.ArgumentParser(description="Encode/decode datafile")

p.add_argument(
"filename",
metavar="FILE",
type=str,
help="Path to input file to read."
)
p.add_argument(
"-d", "--decode",
action="store_true",
help="Decode data"
)

args = p.parse_args()

if len(sys.argv) < 2:
print(f"Usage: {script_name} FILE", file=sys.stderr)
return(1)

data = read_file(args.filename)

if args.decode:
decoded_data = base64.b64decode(data).decode("utf-8")
print(decoded_data)
else:
encoded_data = base64.b64encode(data.encode("utf-8")).decode("utf-8")
print(encoded_data)

return(0)

if __name__ == "__main__":
retval = 0
retval = main()
sys.exit(retval)
36 changes: 36 additions & 0 deletions captcha/_misc/example.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<h1>OpenPMIx Developer (2025) Zoom meetings</h1>

<p>
3:00 PM - 4:00 PM (1 hour) US Eastern (US and Canada)<br>
Every First Thursday @ 3:00 AM ET until 12/31/2025
</p>

<p>
<b>Join ZoomGov Meeting</b><br>
<a href="https://www.zoomgov.com/j/1618899775?pwd=gjBRqsPpi4KbzuMy3MGQ5ccQhRSo9f.1">https://www.zoomgov.com/j/1618899775?pwd=gjBRqsPpi4KbzuMy3MGQ5ccQhRSo9f.1</a>
<br>
<br>
Meeting ID: 161 889 9775<br>
Passcode: 638173<br>
</p>

<br>
<br>

<b>Call in (audio only)</b><br>
+16692545252,,1618899775# US (San Jose)<br>
+16469641167,,1618899775# US (US Spanish Line)<br>
+16468287666,,1618899775# US (New York)<br>
<br>
Dial by your location:
<br>+1 669 254 5252 US (San Jose)
<br>+1 646 964 1167 US (US Spanish Line)
<br>+1 646 828 7666 US (New York)
<br>+1 669 216 1590 US (San Jose)
<br>+1 415 449 4000 US (US Spanish Line)
<br>+1 551 285 1373 US (New Jersey)
<br>Find your local number: <a href="https://www.zoomgov.com/u/adCsm9MBfx">https://www.zoomgov.com/u/adCsm9MBfx</a>

<br>
Meeting ID: 161 889 9775<br>
<br>
31 changes: 25 additions & 6 deletions captcha/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hCaptcha Demo</title>
<title>OpenPMIx Developer (2025) Zoom meeting info</title>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
text-align: left;
margin-top: 50px;
}
form {
Expand All @@ -27,8 +27,14 @@
</head>
<body>
<div class="container">
<h1>hCaptcha Demo</h1>
<p>Please complete the CAPTCHA to proceed.</p>
<h1>OpenPMIx Developer (2025) Zoom meeting info</h1>
<p>
11:00 AM - 12:00 PM (1 hour) US Eastern<br>
Every First Thursday @11:00 AM ET until 12/31/2025
</p>
<p>
Complete the CAPTCHA then submit the form to get the meeting links.
</p>
<form id="captcha-form">
<div class="h-captcha" data-sitekey="e924a3fe-9d8e-482e-b2d8-2a5c308a8746"></div>
<br>
Expand All @@ -42,8 +48,21 @@ <h1>hCaptcha Demo</h1>
const messageDiv = document.getElementById('message');

form.addEventListener('submit', (e) => {
e.preventDefault();
messageDiv.textContent = "Form submitted successfully! (This is just a demo.)";
e.preventDefault(); // Prevent traditional form submission

// Simulate CAPTCHA validation
const hCaptchaResponse = hcaptcha.getResponse();
if (hCaptchaResponse) {
// Generate a unique token
const token = btoa(Date.now() + Math.random().toString()).substring(0, 16);

// Redirect to success.html with token as a query parameter
window.location.href = `success.html?auth=${token}`;
} else {
// Display an error if CAPTCHA isn't completed
messageDiv.textContent = "Please complete the CAPTCHA before proceeding.";
messageDiv.style.color = "red";
}
});
</script>
</body>
Expand Down
40 changes: 40 additions & 0 deletions captcha/success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenPMIx Developer meetings</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: left;
margin-top: 50px;
}
</style>
</head>
<body>
<div id="content"></div>

<script>
// Get the URL parameters
const params = new URLSearchParams(window.location.search);
const token = params.get('auth');

if (token) {
// Base64 encoded string
const encodedData = `PGgxPk9wZW5QTUl4IERldmVsb3BlciAoMjAyNSkgWm9vbSBtZWV0aW5nczwvaDE+Cgo8cD4KMzowMCBQTSAtIDQ6MDAgUE0gKDEgaG91cikgVVMgRWFzdGVybiAoVVMgYW5kIENhbmFkYSk8YnI+CkV2ZXJ5IEZpcnN0IFRodXJzZGF5IEAgMzowMCBBTSBFVCB1bnRpbCAxMi8zMS8yMDI1CjwvcD4KCjxwPgo8Yj5Kb2luIFpvb21Hb3YgTWVldGluZzwvYj48YnI+CjxhIGhyZWY9Imh0dHBzOi8vd3d3Lnpvb21nb3YuY29tL2ovMTYxODg5OTc3NT9wd2Q9Z2pCUnFzUHBpNEtienVNeTNNR1E1Y2NRaFJTbzlmLjEiPmh0dHBzOi8vd3d3Lnpvb21nb3YuY29tL2ovMTYxODg5OTc3NT9wd2Q9Z2pCUnFzUHBpNEtienVNeTNNR1E1Y2NRaFJTbzlmLjE8L2E+Cjxicj4KPGJyPgpNZWV0aW5nIElEOiAxNjEgODg5IDk3NzU8YnI+ClBhc3Njb2RlOiA2MzgxNzM8YnI+CjwvcD4KCjxicj4KPGJyPgoKPGI+Q2FsbCBpbiAoYXVkaW8gb25seSk8L2I+PGJyPgogICsxNjY5MjU0NTI1MiwsMTYxODg5OTc3NSMgVVMgKFNhbiBKb3NlKTxicj4KICArMTY0Njk2NDExNjcsLDE2MTg4OTk3NzUjIFVTIChVUyBTcGFuaXNoIExpbmUpPGJyPgogICsxNjQ2ODI4NzY2NiwsMTYxODg5OTc3NSMgVVMgKE5ldyBZb3JrKTxicj4KPGJyPgpEaWFsIGJ5IHlvdXIgbG9jYXRpb246CiA8YnI+KzEgNjY5IDI1NCA1MjUyIFVTIChTYW4gSm9zZSkKIDxicj4rMSA2NDYgOTY0IDExNjcgVVMgKFVTIFNwYW5pc2ggTGluZSkKIDxicj4rMSA2NDYgODI4IDc2NjYgVVMgKE5ldyBZb3JrKQogPGJyPisxIDY2OSAyMTYgMTU5MCBVUyAoU2FuIEpvc2UpCiA8YnI+KzEgNDE1IDQ0OSA0MDAwIFVTIChVUyBTcGFuaXNoIExpbmUpCiA8YnI+KzEgNTUxIDI4NSAxMzczIFVTIChOZXcgSmVyc2V5KQogPGJyPkZpbmQgeW91ciBsb2NhbCBudW1iZXI6IDxhIGhyZWY9Imh0dHBzOi8vd3d3Lnpvb21nb3YuY29tL3UvYWRDc205TUJmeCI+aHR0cHM6Ly93d3cuem9vbWdvdi5jb20vdS9hZENzbTlNQmZ4PC9hPgoKPGJyPgpNZWV0aW5nIElEOiAxNjEgODg5IDk3NzU8YnI+Cjxicj4K`;

// Decode the Base64 string
const decodedData = atob(encodedData);

// Token exists, display success message
document.getElementById('content').innerHTML = `
<p>${decodedData}</p>
`;
} else {
// Token is missing, redirect back to the main page
window.location.href = "index.html";
}
</script>
</body>
</html>

0 comments on commit ea23871

Please sign in to comment.