site

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

index.html (8306B)


      1 <!DOCTYPE html>
      2 <html lang="en">
      3  <head>
      4   <link rel="stylesheet" href="/style.css" type="text/css">
      5   <meta charset="utf-8">
      6   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      7   <meta name="viewport" content="width=device-width, initial-scale=1.0">
      8   <link rel="stylesheet" type="text/css" href="/style.css">
      9   <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🏕️</text></svg>">
     10   <title></title>
     11  </head>
     12  <body>
     13   <div id="page-wrapper">
     14    <div id="header" role="banner">
     15     <header class="banner">
     16      <div id="banner-text">
     17       <span class="banner-title"><a href="/">beauhilton</a></span>
     18      </div>
     19     </header>
     20     <nav>
     21      <a href="/about">about</a>
     22 <a href="/now">now</a>
     23 <a href="/thanks">thanks</a>
     24 <a class="nav-active" href="/posts">posts</a>
     25 <a href="https://notes.beauhilton.com">notes</a>
     26 <a href="https://talks.beauhilton.com">talks</a>
     27 <a href="https://git.beauhilton.com">git</a>
     28 <a href="/contact">contact</a>
     29 <a href="/atom.xml">rss</a>
     30     </nav>
     31    </div>
     32    <main>
     33     <h1>
     34      password protecting static web pages
     35     </h1>
     36     <p>
     37      <time id="post-date">2023-01-12</time>
     38     </p>
     39     <p id="post-excerpt">
     40      Static site generators, by and large, do not have support for password protecting individual pages. 
     41 This post shows a strategy that works for any SSG and does not require server access controls.
     42     </p>
     43     <p>
     44      <a href="https://github.com/Greenheart/pagecrypt">PageCrypt</a> is
     45 the tool I went with. It’s a TypeScript library that uses the native
     46 <code>Web Crypto API</code> to encrypt html pages, stuff the encrypted
     47 gobbledigook behind a simple password form, and decrypt the page when
     48 the correct key is given.
     49     </p>
     50     <p>
     51      I’m using the CLI version, but there are also native TypeScript and
     52 Node.js versions.
     53     </p>
     54     <p>
     55      The basic idea is this, taken from their docs:
     56     </p>
     57     <pre tabindex="0"><code class="language-sh">npx pagecrypt <span class="hl opt">&lt;</span>src<span class="hl opt">&gt; &lt;</span>dest<span class="hl opt">&gt; [</span>password<span class="hl opt">] [</span>options<span class="hl opt">]</span>
     58 </code></pre>
     59     <p>
     60      Below is an excerpt from a <code>package.json</code> for a Hugo site
     61 I maintain. First I build the site, then run the <code>postbuild</code>
     62 command, which stitches together a piece of code that creates a list of
     63 all the pages we’d like to encrypt and another bit that does the
     64 encryption proper.
     65     </p>
     66     <pre tabindex="0"><code class="language-json"><span class="hl kwa">{</span>
     67     <span class="hl kwc">"make-pw-files-file"</span><span class="hl opt">:</span> <span class="hl sng">"grep -rlF 'password_required: true' content | sed s+</span><span class="hl esc">\\</span><span class="hl sng">.md+</span><span class="hl esc">\\</span><span class="hl sng">/index.html+g | sed s+content/+public/+g &gt; pw_file"</span><span class="hl opt">,</span>
     68     <span class="hl kwc">"protect-files"</span><span class="hl opt">:</span> <span class="hl sng">"cat pw_file | while read f || [[ -n $f ]]; do npx pagecrypt $f $f $PAGECRYPT; done"</span><span class="hl opt">,</span>
     69     <span class="hl kwc">"postbuild"</span><span class="hl opt">:</span> <span class="hl sng">"npm run -s make-pw-files-file &amp;&amp; npm run -s protect-files "</span><span class="hl opt">,</span>
     70 <span class="hl kwa">}</span>
     71 </code></pre>
     72 <br>
     73     <h2>
     74      <code>make-pw-files-file</code>
     75     </h2>
     76     <pre tabindex="0"><code class="language-sh"><span class="hl kwc">grep</span> <span class="hl kwb">-rlF</span> <span class="hl sng">'password_required: true'</span> content <span class="hl opt">|</span> <span class="hl kwc">sed</span> s<span class="hl opt">+</span><span class="hl esc">\\</span>.md<span class="hl opt">+</span><span class="hl esc">\\</span><span class="hl opt">/</span>index.html<span class="hl opt">+</span>g <span class="hl opt">|</span> <span class="hl kwc">sed</span> s<span class="hl opt">+</span>content<span class="hl opt">/+</span>public<span class="hl opt">/+</span>g <span class="hl opt">&gt;</span> pw_file
     77 </code></pre>
     78     <p>
     79      This part uses a few standard Unix commands, the ubiquitous
     80 <code>grep</code> and <code>sed</code>.
     81     </p>
     82     <p>
     83      First it creates a list of markdown source files that have the header
     84 <code>password_required: true</code>.
     85     </p>
     86     <p>
     87      Here’s how that part works:
     88     </p>
     89     <pre tabindex="0"><code class="language-sh"><span class="hl kwc">grep</span> <span class="hl kwb">-rlF</span> <span class="hl sng">"text you're looking for"</span> path<span class="hl opt">/</span>to<span class="hl opt">/</span>files<span class="hl opt">/</span>of<span class="hl opt">/</span>interest
     90 </code></pre>
     91     <ul>
     92      <li>
     93       <code>-r</code> or <code>--recursive</code> searches though the
     94 sub-directories of whichever path you gave it
     95      </li>
     96      <li>
     97       <code>l</code> or <code>--files-with-matches</code> prints the
     98 filenames of any files that match the search
     99      </li>
    100      <li>
    101       <code>F</code> or <code>--fixed-strings</code> searches for the
    102 exact string you ask for, not using regular expressions
    103      </li>
    104     </ul>
    105     <p>
    106      This will output something like:
    107     </p>
    108     <pre tabindex="0"><code class="language-sh">content<span class="hl opt">/</span>file0.md
    109 content<span class="hl opt">/</span>file7.md
    110 content<span class="hl opt">/</span>yet_another_file.md
    111 </code></pre>
    112     <p>
    113      Next it passes that to <code>sed</code>, which replaces
    114 <code>.md</code> with <code>/index.html</code>* resulting in:
    115     </p>
    116     <pre tabindex="0"><code class="language-sh">content<span class="hl opt">/</span>file<span class="hl opt">/</span>index.html
    117 content<span class="hl opt">/</span>file<span class="hl num">7</span><span class="hl opt">/</span>index.html
    118 content<span class="hl opt">/</span>yet_another_file<span class="hl opt">/</span>index.html
    119 </code></pre>
    120     <p>
    121      * you’d have to adjust this command if you aren’t using a
    122 page-per-folder style
    123     </p>
    124     <p>
    125      Then it switches out <code>content/</code> with <code>public/</code>,
    126 leading to the final list:
    127     </p>
    128     <pre tabindex="0"><code class="language-sh">public<span class="hl opt">/</span>file<span class="hl opt">/</span>index.html
    129 public<span class="hl opt">/</span>file<span class="hl num">7</span><span class="hl opt">/</span>index.html
    130 public<span class="hl opt">/</span>yet_another_file<span class="hl opt">/</span>index.html
    131 </code></pre>
    132     <p>
    133      and sends those to a file.
    134     </p>
    135     <p>
    136      Sending the list to a file is not necessary, could just pipe it again
    137 to the stuff in <code>protect-files</code>, but I was getting itchy at
    138 how long the command was getting.
    139     </p>
    140     <br>
    141     <h2>
    142      <code>protect-files</code>
    143     </h2>
    144     <pre tabindex="0"><code class="language-sh"><span class="hl kwc">cat</span> pw_file <span class="hl opt">|</span> <span class="hl kwa">while</span> <span class="hl kwb">read</span> f <span class="hl opt">|| [[</span> <span class="hl kwb">-n</span> <span class="hl kwd">$f</span> <span class="hl opt">]];</span> <span class="hl kwa">do</span> npx pagecrypt <span class="hl kwd">$f $f $PAGECRYPT</span><span class="hl opt">;</span> <span class="hl kwa">done</span>
    145 </code></pre>
    146     <p>
    147      This part emits the contents of <code>pw_file</code>, then loops
    148 through each line (<code>while read f || [[ -n $f ]]</code>) and
    149 encrypts the corresponding file (<code>do npx pagecrypt</code>), finally
    150 saving it back to itself (<code>$f $f</code>).
    151     </p>
    152     <p>
    153      <code>$PAGECRYPT</code> is an environment variable that specifies the
    154 password.
    155     </p>
    156     <p>
    157      You could also set a password-per-page, either auto-generated (but
    158 you’d have to figure out how to get that password to your users), or
    159 using something like another header value in the <code>.md</code> files
    160 that you grep for and save somewhere. For my use case, this is simpler
    161 and scales well.
    162     </p>
    163     <br>
    164     <h2>
    165      and that’s it
    166     </h2>
    167     <p>
    168      As always, <a href="../../contact">let me know</a> if you have any
    169 questions or know of a better way.
    170     </p>
    171    </main>
    172    <div id="footnotes"></div>
    173    <footer></footer>
    174   </div>
    175  </body>
    176 </html>