site

files for beauhilton.com
git clone https://git.beauhilton.com/site.git
Log | Files | Refs

pagecrypt.md (3651B)


      1 # password protecting static web pages
      2 
      3 <time id="post-date">2023-01-12</time>
      4 
      5 <p id="post-excerpt">
      6 Static site generators, by and large, do not have support for password protecting individual pages. 
      7 This post shows a strategy that works for any SSG and does not require server access controls.
      8 </p>
      9 
     10 [PageCrypt](https://github.com/Greenheart/pagecrypt) is the tool I went with.
     11 It's a TypeScript library that uses the native `Web Crypto API` to encrypt html pages,
     12 stuff the encrypted gobbledigook behind a simple password form,
     13 and decrypt the page when the correct key is given.
     14 
     15 I'm using the CLI version, but there are also native TypeScript and Node.js versions. 
     16 
     17 The basic idea is this, taken from their docs:
     18 
     19 ```sh
     20 npx pagecrypt <src> <dest> [password] [options]
     21 ```
     22 
     23 Below is an excerpt from a `package.json` for a Hugo site I maintain.
     24 First I build the site, then run the `postbuild` command, 
     25 which stitches together a piece of code that creates a list of all the pages we'd like to encrypt
     26 and another bit that does the encryption proper.
     27 
     28 ```json
     29 {
     30     "make-pw-files-file": "grep -rlF 'password_required: true' content | sed s+\\.md+\\/index.html+g | sed s+content/+public/+g > pw_file",
     31     "protect-files": "cat pw_file | while read f || [[ -n $f ]]; do npx pagecrypt $f $f $PAGECRYPT; done",
     32     "postbuild": "npm run -s make-pw-files-file && npm run -s protect-files ",
     33 }
     34 ```
     35 
     36 <br>
     37 
     38 ## `make-pw-files-file` 
     39 
     40 ```sh
     41 grep -rlF 'password_required: true' content | sed s+\\.md+\\/index.html+g | sed s+content/+public/+g > pw_file
     42 ```
     43 This part uses a few standard Unix commands, the ubiquitous `grep` and `sed`. 
     44 
     45 First it creates a list of markdown source files that have the header `password_required: true`.
     46 
     47 Here's how that part works:
     48 
     49 ```sh
     50 grep -rlF "text you're looking for" path/to/files/of/interest
     51 ```
     52 
     53 - `-r` or `--recursive` searches though the sub-directories of whichever path you gave it
     54 - `l` or `--files-with-matches` prints the filenames of any files that match the search
     55 - `F` or `--fixed-strings` searches for the exact string you ask for, not using regular expressions
     56 
     57 This will output something like:
     58 
     59 ```sh
     60 content/file0.md
     61 content/file7.md
     62 content/yet_another_file.md
     63 ```
     64 
     65 Next it passes that to `sed`, which replaces `.md` with `/index.html`\* 
     66 resulting in:
     67 
     68 ```sh
     69 content/file/index.html
     70 content/file7/index.html
     71 content/yet_another_file/index.html
     72 ```
     73 
     74 \* you'd have to adjust this command if you aren't using a page-per-folder style
     75 
     76 Then it switches out `content/` with `public/`, leading to the final list:
     77 
     78 
     79 ```sh
     80 public/file/index.html
     81 public/file7/index.html
     82 public/yet_another_file/index.html
     83 ```
     84 
     85 and sends those to a file. 
     86 
     87 Sending the list to a file is not necessary,
     88 could just pipe it again to the stuff in `protect-files`,
     89 but I was getting itchy at how long the command was getting.
     90 
     91 <br>
     92 
     93 ## `protect-files`
     94 
     95 ```sh
     96 cat pw_file | while read f || [[ -n $f ]]; do npx pagecrypt $f $f $PAGECRYPT; done
     97 ```
     98 
     99 This part emits the contents of `pw_file`,
    100 then loops through each line (`while read f || [[ -n $f ]]`)
    101 and encrypts the corresponding file (`do npx pagecrypt`), 
    102 finally saving it back to itself (`$f $f`).
    103 
    104 `$PAGECRYPT` is an environment variable that specifies the password.
    105 
    106 You could also set a password-per-page, 
    107 either auto-generated (but you'd have to figure out how to get that password to your users), 
    108 or using something like another header value in the `.md` files that you grep for and save somewhere.
    109 For my use case, this is simpler and scales well.
    110 
    111 <br>
    112 
    113 ## and that's it
    114 
    115 As always, [let me know](../../contact) if you have any questions or know of a better way.