diff --git a/captcha/_misc/README.md b/captcha/_misc/README.md new file mode 100644 index 0000000..3ea9a89 --- /dev/null +++ b/captcha/_misc/README.md @@ -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` + diff --git a/captcha/_misc/encode.py b/captcha/_misc/encode.py new file mode 100755 index 0000000..170fb94 --- /dev/null +++ b/captcha/_misc/encode.py @@ -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) diff --git a/captcha/_misc/example.dat b/captcha/_misc/example.dat new file mode 100644 index 0000000..8688648 --- /dev/null +++ b/captcha/_misc/example.dat @@ -0,0 +1,36 @@ +

OpenPMIx Developer (2025) Zoom meetings

+ +

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

+ +

+Join ZoomGov Meeting
+https://www.zoomgov.com/j/1618899775?pwd=gjBRqsPpi4KbzuMy3MGQ5ccQhRSo9f.1 +
+
+Meeting ID: 161 889 9775
+Passcode: 638173
+

+ +
+
+ +Call in (audio only)
+ +16692545252,,1618899775# US (San Jose)
+ +16469641167,,1618899775# US (US Spanish Line)
+ +16468287666,,1618899775# US (New York)
+
+Dial by your location: +
+1 669 254 5252 US (San Jose) +
+1 646 964 1167 US (US Spanish Line) +
+1 646 828 7666 US (New York) +
+1 669 216 1590 US (San Jose) +
+1 415 449 4000 US (US Spanish Line) +
+1 551 285 1373 US (New Jersey) +
Find your local number: https://www.zoomgov.com/u/adCsm9MBfx + +
+Meeting ID: 161 889 9775
+
diff --git a/captcha/index.html b/captcha/index.html index a452a37..57c20a4 100644 --- a/captcha/index.html +++ b/captcha/index.html @@ -3,12 +3,12 @@ - hCaptcha Token Redirect + OpenPMIx Developer (2025) Zoom meeting info @@ -21,12 +21,15 @@ 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 = ` -

Success!

-

You have successfully completed the CAPTCHA and accessed this page.

-

Info for 2025

-

This is where the info for 2025 meeting would go.

+

${decodedData}

`; } else { // Token is missing, redirect back to the main page