<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Go on Kiril Vladimirov</title>
    <link>https://vld.bg/tags/go/</link>
    <description>Recent content in Go on Kiril Vladimirov</description>
    <generator>Hugo -- 0.168.0</generator>
    <language>en</language>
    <lastBuildDate>Sun, 19 May 2024 23:02:28 +0300</lastBuildDate>
    <atom:link href="https://vld.bg/tags/go/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Retrieving file timestamps in Go</title>
      <link>https://vld.bg/articles/go-stat/</link>
      <pubDate>Sun, 19 May 2024 23:02:28 +0300</pubDate>
      <guid>https://vld.bg/articles/go-stat/</guid>
      <description>&lt;p&gt;Recently, a friend of mine needed to get the creation time of a file in a Go
project. There&amp;rsquo;s &lt;a href=&#34;https://pkg.go.dev/os#Stat&#34; target=&#34;_blank&#34;&gt;os.Stat&lt;/a&gt;
, so how hard can it be,
right?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;os.Stat&lt;/code&gt; returns &lt;a href=&#34;https://pkg.go.dev/io/fs#FileInfo&#34; target=&#34;_blank&#34;&gt;fs.FileInfo&lt;/a&gt;
 which is an
interface with six methods:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;FileInfo&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;interface&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#447fcf&#34;&gt;Name&lt;/span&gt;()&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;string&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;       &lt;/span&gt;&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;// base name of the file&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#447fcf&#34;&gt;Size&lt;/span&gt;()&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;int64&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;// length in bytes for regular files; [..]&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#447fcf&#34;&gt;Mode&lt;/span&gt;()&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;FileMode&lt;span style=&#34;color:#666&#34;&gt;     &lt;/span&gt;&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;// file mode bits&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#447fcf&#34;&gt;ModTime&lt;/span&gt;()&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;time.Time&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;// modification time&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#447fcf&#34;&gt;IsDir&lt;/span&gt;()&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;bool&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;// abbreviation for Mode().IsDir()&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#447fcf&#34;&gt;Sys&lt;/span&gt;()&lt;span style=&#34;color:#666&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;any&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;           &lt;/span&gt;&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;// underlying data source (can return nil)&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#666&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Time of last modification (mtime) is the only time reported by this interface,
though. There is no time of last access (atime), creation (btime), nor status
change (ctime).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Recently, a friend of mine needed to get the creation time of a file in a Go
project. There&rsquo;s <a href="https://pkg.go.dev/os#Stat" target="_blank">os.Stat</a>
, so how hard can it be,
right?</p>
<p><code>os.Stat</code> returns <a href="https://pkg.go.dev/io/fs#FileInfo" target="_blank">fs.FileInfo</a>
 which is an
interface with six methods:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>FileInfo<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">interface</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#447fcf">Name</span>()<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">string</span><span style="color:#666">       </span><span style="color:#999;font-style:italic">// base name of the file</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#447fcf">Size</span>()<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">int64</span><span style="color:#666">        </span><span style="color:#999;font-style:italic">// length in bytes for regular files; [..]</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#447fcf">Mode</span>()<span style="color:#666"> </span>FileMode<span style="color:#666">     </span><span style="color:#999;font-style:italic">// file mode bits</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#447fcf">ModTime</span>()<span style="color:#666"> </span>time.Time<span style="color:#666"> </span><span style="color:#999;font-style:italic">// modification time</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#447fcf">IsDir</span>()<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">bool</span><span style="color:#666">        </span><span style="color:#999;font-style:italic">// abbreviation for Mode().IsDir()</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#447fcf">Sys</span>()<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">any</span><span style="color:#666">           </span><span style="color:#999;font-style:italic">// underlying data source (can return nil)</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span></code></pre></div><p>Time of last modification (mtime) is the only time reported by this interface,
though. There is no time of last access (atime), creation (btime), nor status
change (ctime).</p>
<h2 id="sys">Sys()</h2>
<p>Did you notice that <code>Sys()</code> method there? Yeah, me neither. This poorly
documented method returns raw data for that file, as returned by the operating
system. That means the underlying type of the result will be different per-OS.
In our case, it&rsquo;s a software intended to run exclusively on linux, so let&rsquo;s see
what happens there.</p>
<p>Well, in Linux, it returns a pointer to <a href="https://pkg.go.dev/syscall#Stat_t" target="_blank">syscall.Stat_t</a>
:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>Stat_t<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">struct</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Dev<span style="color:#666">       </span><span style="color:#6ab825;font-weight:bold">uint64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Ino<span style="color:#666">       </span><span style="color:#6ab825;font-weight:bold">uint64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Nlink<span style="color:#666">     </span><span style="color:#6ab825;font-weight:bold">uint64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Mode<span style="color:#666">      </span><span style="color:#6ab825;font-weight:bold">uint32</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Uid<span style="color:#666">       </span><span style="color:#6ab825;font-weight:bold">uint32</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Gid<span style="color:#666">       </span><span style="color:#6ab825;font-weight:bold">uint32</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>X__pad0<span style="color:#666">   </span><span style="color:#6ab825;font-weight:bold">int32</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Rdev<span style="color:#666">      </span><span style="color:#6ab825;font-weight:bold">uint64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Size<span style="color:#666">      </span><span style="color:#6ab825;font-weight:bold">int64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Blksize<span style="color:#666">   </span><span style="color:#6ab825;font-weight:bold">int64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Blocks<span style="color:#666">    </span><span style="color:#6ab825;font-weight:bold">int64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Atim<span style="color:#666">      </span>Timespec<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Mtim<span style="color:#666">      </span>Timespec<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Ctim<span style="color:#666">      </span>Timespec<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>X__unused<span style="color:#666"> </span>[<span style="color:#3677a9">3</span>]<span style="color:#6ab825;font-weight:bold">int64</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span></code></pre></div><p>Great, we have atime, mtime and ctime. We&rsquo;ve almost declared a win, until
realizing that ctime is not creation time. It&rsquo;s the time of last status change,
which means it records any change of file&rsquo;s metadata. What we seek is creation
time a.k.a. birth time. Yep, I&rsquo;ve just used almost 300 words to tell you
that with <code>os.Stat</code> you can NOT get the creation time of a file.</p>
<h1 id="statx2">statx(2)</h1>
<p>To be fair, you would not be able to get the creation time using stat in C,
either. Not to mention that most of the filesystems do not keep record of it.
That was surprising to me - fat32, ext2, ext3 nor reiserfs store when a file
has been created. Naturally, no such information is exposed by stat(2).
Modern filesystems do, though.</p>
<p>After years of planning a new system call is finally merged into the Linux kernel
and released with 4.11 in 2017. That&rsquo;s years after the first released version
of Go and five after 1.0. That explains why the standard library does not
provide a straightforward way to obtain btime.</p>
<h1 id="golangorgxsysunix">golang.org/x/sys/unix</h1>
<p>The standard library might be a bit conservative in adding new things (although
things are starting to move now with the new
<a href="https://go.dev/blog/randv2" target="_blank">math/rand/v2</a>
), but that does not mean the Go team
is sleeping. The <code>golang.org/x/</code> packages are outside the main Go tree and are
under way looser constraints for backwards compatibility. One of these packages
is <a href="https://pkg.go.dev/golang.org/x/sys/unix" target="_blank">golang.org/x/sys/unix</a>
. The <code>sys</code>
part is described as &ldquo;packages for making system calls&rdquo;, so do not expect the
easiest to use API you&rsquo;ve ever seen.</p>
<p>Sure enough, there&rsquo;s a wrapper function of the statx syscall:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">func</span><span style="color:#666"> </span><span style="color:#447fcf">Statx</span>(dirfd<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">int</span>,<span style="color:#666"> </span>path<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">string</span>,<span style="color:#666"> </span>flags<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">int</span>,<span style="color:#666"> </span>mask<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">int</span>,<span style="color:#666"> </span>stat<span style="color:#666"> </span>*Statx_t)<span style="color:#666"> </span>(err<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">error</span>)<span style="color:#666">
</span></span></span></code></pre></div><p>As expected the API is pretty much the same as with the syscall it wraps:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">int</span> <span style="color:#447fcf">statx</span>(<span style="color:#6ab825;font-weight:bold">int</span> dirfd, <span style="color:#6ab825;font-weight:bold">const</span> <span style="color:#6ab825;font-weight:bold">char</span> *<span style="color:#6ab825;font-weight:bold">restrict</span> pathname, <span style="color:#6ab825;font-weight:bold">int</span> flags,
</span></span><span style="display:flex;"><span>          <span style="color:#6ab825;font-weight:bold">unsigned</span> <span style="color:#6ab825;font-weight:bold">int</span> mask, <span style="color:#6ab825;font-weight:bold">struct</span> statx *<span style="color:#6ab825;font-weight:bold">restrict</span> statxbuf);
</span></span></code></pre></div><ul>
<li>dirfd is a file descriptor identifying the directory to search from if
provided path is relative (if is <code>AT_FDCWD</code> the call operates on the
current working directory).</li>
<li>path is obviously the filename we&rsquo;re querying. Unless when empty and
<code>AT_EMPTY_PATH</code> is in mask, in that case file/directory pointed to by
dirfd is what we&rsquo;re querying.</li>
<li>flags can be used to influence a pathname-based lookup and what sort of
synchronization the kernel will do when querying. When in doubt, use
<code>AT_STATX_SYNC_AS_STAT</code>.</li>
<li>mask is used to tell which fields we are interested in, so that the kernel
does not bother populating the rest. The one we care about to is
<code>STATX_BTIME</code>. If it&rsquo;s there (or <code>STATX_ALL</code> which pretty much includes
everything), then the creation time will be there.</li>
<li>stat is being populated be the syscall when the returned error is nil.</li>
</ul>
<p>All of those <code>SCREAMING_SNAKE_CASE</code> flags listed above are exposed as constants in
the same package.</p>
<p><code>unix.Statx_t</code> is a fairly big struct (matching the statx struct obviously)
that has all of the timestamps we seek:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>Statx_t<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">struct</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#999;font-style:italic">// ...</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Atime<span style="color:#666">            </span>StatxTimestamp<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Btime<span style="color:#666">            </span>StatxTimestamp<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Ctime<span style="color:#666">            </span>StatxTimestamp<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Mtime<span style="color:#666">            </span>StatxTimestamp<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">    </span><span style="color:#cd2828;font-weight:bold">//..</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span></code></pre></div><p><code>StatxTimestamp</code> has <code>Sec</code> and <code>Nsec</code> fields which is enough to create a
<code>time.Time</code> value using <code>time.Unix()</code>. So, the whole thing might look something
like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">var</span><span style="color:#666"> </span>statx<span style="color:#666"> </span>unix.Statx_t<span style="color:#666">
</span></span></span><span style="display:flex;"><span>err<span style="color:#666"> </span>:=<span style="color:#666"> </span>unix.<span style="color:#447fcf">Statx</span>(unix.AT_FDCWD,<span style="color:#666"> </span>name,<span style="color:#666"> </span>unix.AT_STATX_SYNC_AS_STAT,<span style="color:#666"> </span>unix.STATX_BTIME,<span style="color:#666"> </span>&amp;statx)<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">if</span><span style="color:#666"> </span>err<span style="color:#666"> </span>!=<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">nil</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">    </span>log.<span style="color:#447fcf">Fatal</span>(err)<span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>btime<span style="color:#666"> </span>:=<span style="color:#666"> </span>time.<span style="color:#447fcf">Unix</span>(statx.Btime.Sec,<span style="color:#666"> </span><span style="color:#24909d">int64</span>(statx.Btime.Nsec))<span style="color:#666">
</span></span></span><span style="display:flex;"><span>fmt.<span style="color:#447fcf">Printf</span>(<span style="color:#ed9d13">&#34;%s is created at %v\n&#34;</span>,<span style="color:#666"> </span>name,<span style="color:#666"> </span>btime)<span style="color:#666">
</span></span></span></code></pre></div><h2 id="easy-right">Easy, right?</h2>
<p>Don&rsquo;t agree? Yeah, me neither. A saner person would just npm the problem and
look for a package exposing file times in a proper, easy-to-use way. I found
this, which looks legit (only checked for GOOS=linux):
<a href="https://github.com/djherbis/times" target="_blank">github.com/djherbis/times</a>
.</p>
<p>I mean it. A third-party package with a higher-level <strong>portable</strong> API is the
way to go in the real world. It&rsquo;s good to know what&rsquo;s happening underneath, but
please beware of NIH syndrome.</p>
<p>Achieving the same result in Python isn&rsquo;t much easier. The result from <code>os.stat</code>
might have a <code>st_birthtime</code> on some operating systems (e.g., FreeBSD or
Windows), but not on Linux. Sometimes seemingly trivial problems are tedious to
solve because of weird historical reasons. And sometimes, a deeper dive is
necessary, but practical solutions are often just a package away.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Type for a context value in Go</title>
      <link>https://vld.bg/articles/go-context-type/</link>
      <pubDate>Fri, 08 Dec 2023 19:16:54 +0200</pubDate>
      <guid>https://vld.bg/articles/go-context-type/</guid>
      <description>&lt;p&gt;Using context values in Go appears, at first sight, very trivial and easily
understood. There are pitfalls, though. For instance, what type to use for the
key?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ go doc context WithValue
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;package context // import &amp;#34;context&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;func WithValue(parent Context, key, val any) Context
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    WithValue returns a copy of parent in which the value associated with key is
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    val.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Use context Values only for request-scoped data that transits processes and
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    APIs, not for passing optional parameters to functions.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    The provided key must be comparable and should not be of type string or any
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    other built-in type to avoid collisions between packages using context.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Users of WithValue should define their own types for keys. To avoid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allocating when assigning to an interface{}, context keys often have
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    concrete type struct{}. Alternatively, exported context key variables&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    static type should be a pointer or interface.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This whole article exists because of this:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Using context values in Go appears, at first sight, very trivial and easily
understood. There are pitfalls, though. For instance, what type to use for the
key?</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>❯ go doc context WithValue
</span></span><span style="display:flex;"><span>package context // import &#34;context&#34;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>func WithValue(parent Context, key, val any) Context
</span></span><span style="display:flex;"><span>    WithValue returns a copy of parent in which the value associated with key is
</span></span><span style="display:flex;"><span>    val.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    Use context Values only for request-scoped data that transits processes and
</span></span><span style="display:flex;"><span>    APIs, not for passing optional parameters to functions.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    The provided key must be comparable and should not be of type string or any
</span></span><span style="display:flex;"><span>    other built-in type to avoid collisions between packages using context.
</span></span><span style="display:flex;"><span>    Users of WithValue should define their own types for keys. To avoid
</span></span><span style="display:flex;"><span>    allocating when assigning to an interface{}, context keys often have
</span></span><span style="display:flex;"><span>    concrete type struct{}. Alternatively, exported context key variables&#39;
</span></span><span style="display:flex;"><span>    static type should be a pointer or interface.
</span></span></code></pre></div><p>This whole article exists because of this:</p>
<blockquote>
<p>Users of WithValue should define their own types for keys.</p>
</blockquote>
<p>OK, but what would that type be? It should NOT be a string, but its underlying
type could be one. In fact, over the years, I&rsquo;ve used an internal string type
with a value equal to the full package name:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#999;font-style:italic">// ctxt is a dummy type for storing context values.</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">//</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">// For more information see the documentation of context.WithValue.</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>ctxt<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">string</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">var</span><span style="color:#666"> </span>ctxkey<span style="color:#666"> </span>ctxt<span style="color:#666"> </span>=<span style="color:#666"> </span><span style="color:#ed9d13">&#34;github.com/org/project/pkg&#34;</span><span style="color:#666">
</span></span></span></code></pre></div><p>Now <code>&amp;ctxkey</code> is what we use as context key as suggested by the documentation
in order to avoid useless allocations. Often I would introduce public functions
to take or store their expected value, without dealing with keys, mostly for
testing. Suppose all this package needs a ctx type for is to store values of
type T:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#999;font-style:italic">// FromContext extracts a value of type T from the given context, returning its</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">// zero value if not present.</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">func</span><span style="color:#666"> </span><span style="color:#447fcf">FromContext</span>(ctx<span style="color:#666"> </span>context.Context)<span style="color:#666"> </span>T<span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>v,<span style="color:#666"> </span>ok<span style="color:#666"> </span>:=<span style="color:#666"> </span>ctx.<span style="color:#447fcf">Value</span>(&amp;ctxkey).(T)<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#6ab825;font-weight:bold">if</span><span style="color:#666"> </span>!ok<span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">		</span>v<span style="color:#666"> </span>=<span style="color:#666"> </span><span style="color:#447fcf">NewT</span>()<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>}<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#6ab825;font-weight:bold">return</span><span style="color:#666"> </span>v<span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">// WithValue returns a copy of parent with stored T inside.</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">func</span><span style="color:#666"> </span><span style="color:#447fcf">WithValue</span>(parent<span style="color:#666"> </span>context.Context,<span style="color:#666"> </span>t<span style="color:#666"> </span>T)<span style="color:#666"> </span>context.Context<span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#6ab825;font-weight:bold">return</span><span style="color:#666"> </span>context.<span style="color:#447fcf">WithValue</span>(parent,<span style="color:#666"> </span>&amp;ctxkey,<span style="color:#666"> </span>t)<span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span></code></pre></div><p><code>pkg.FromContext(ctx)</code> takes a context and returns the <code>pkg.T</code> stored inside,
if any, for instance.</p>
<p>The point to store the full package name is merely to ease debugging and/or
tracing. This string is allocated only once, and we are using a pointer to it,
in order not to allocate once per assigning that key to <code>interface{}</code>. If we
want to get fanatical about that, the type of <code>ctxt</code> might be an empty struct:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#999;font-style:italic">// ctxkey is a dummy type for storing context values.</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">//</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">// For more information see the documentation of context.WithValue.</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>ctxkey<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">struct</span>{}<span style="color:#666">
</span></span></span></code></pre></div><p>Now we no longer need to use a pointer to ctxkey and just directly pass
<code>ctxkey{}</code> instead, because a value of <code>struct{}</code> never allocates as its size
is 0 anyways. When debugging you would still see the type of ctxkey, which (as
with any type) contains the full path to it.</p>
<p>I have no memory which tool/use-case didn&rsquo;t work well with <code>struct{}</code> so I&rsquo;ve
started using a string in the first place, but I&rsquo;m back to using <code>struct{}</code>
again these days.</p>
<h2 id="what-about-generics">What about generics?</h2>
<p>Yes, of course. Go has generics now for quite some time, why not sprinkling
some of it to solve that? We could. In fact, there is a proposal that is placed
on hold (at the moment of writing this) in order to give the community time to
play more with generics: <a href="https://github.com/golang/go/issues/49189" target="_blank">https://github.com/golang/go/issues/49189</a>
</p>
<p>If you have tens of packages storing stuff in contexts and are pretty sure that
this makes sense, might as well steal the proposed <code>Key[T]</code> from there.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Simple Unix-like authorization with Go</title>
      <link>https://vld.bg/articles/go-unix-auth/</link>
      <pubDate>Sun, 03 Sep 2023 23:12:00 +0300</pubDate>
      <guid>https://vld.bg/articles/go-unix-auth/</guid>
      <description>&lt;p&gt;Modern complex projects often require sophisticated access-control
functionality, like Identity and Access Management
(&lt;a href=&#34;https://en.wikipedia.org/wiki/Identity_management&#34; target=&#34;_blank&#34;&gt;IAM&lt;/a&gt;
) systems. Sometimes,
however there are cases where simple solutions might just cut it. Let&amp;rsquo;s cover
one of these cases.&lt;/p&gt;
&lt;p&gt;Say we&amp;rsquo;re working on a system for publishing  articles, allowing discussion
below each article, with the following requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moderators can edit all articles and comments&lt;/li&gt;
&lt;li&gt;Authors can create articles and edit only those created by themselves&lt;/li&gt;
&lt;li&gt;Users can read articles and post comments about them&lt;/li&gt;
&lt;li&gt;Any user-type can edit and delete their own profile&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We could define different user-levels as a bit-mask and store that alongside
user&amp;rsquo;s information (e.g. users table in the database):&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Modern complex projects often require sophisticated access-control
functionality, like Identity and Access Management
(<a href="https://en.wikipedia.org/wiki/Identity_management" target="_blank">IAM</a>
) systems. Sometimes,
however there are cases where simple solutions might just cut it. Let&rsquo;s cover
one of these cases.</p>
<p>Say we&rsquo;re working on a system for publishing  articles, allowing discussion
below each article, with the following requirements:</p>
<ul>
<li>Moderators can edit all articles and comments</li>
<li>Authors can create articles and edit only those created by themselves</li>
<li>Users can read articles and post comments about them</li>
<li>Any user-type can edit and delete their own profile</li>
</ul>
<p>We could define different user-levels as a bit-mask and store that alongside
user&rsquo;s information (e.g. users table in the database):</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">const</span><span style="color:#666"> </span>(<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>user<span style="color:#666"> </span>=<span style="color:#666"> </span><span style="color:#3677a9">1</span><span style="color:#666"> </span>&lt;&lt;<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">iota</span><span style="color:#666">    </span><span style="color:#999;font-style:italic">// 0001</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>author<span style="color:#666">              </span><span style="color:#999;font-style:italic">// 0010</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>moderator<span style="color:#666">           </span><span style="color:#999;font-style:italic">// 0100</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>self<span style="color:#666">                </span><span style="color:#999;font-style:italic">// 1000</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>)<span style="color:#666">
</span></span></span></code></pre></div><p>Each of the actions listed above can now be expressed as a list of user types
allowed to perform them, by using the <a href="https://en.wikipedia.org/wiki/Bitwise_operation#OR" target="_blank">bitwise
OR</a>
 operator, omitting
actions allowed by everybody even without an account:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">const</span><span style="color:#666"> </span>(<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>CreateArticle<span style="color:#666"> </span>=<span style="color:#666"> </span>author<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>UpdateArticle<span style="color:#666"> </span>=<span style="color:#666"> </span>self<span style="color:#666"> </span>|<span style="color:#666"> </span>moderator<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>DeleteArticle<span style="color:#666"> </span>=<span style="color:#666"> </span>self<span style="color:#666"> </span>|<span style="color:#666"> </span>moderator<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>CreateComment<span style="color:#666"> </span>=<span style="color:#666"> </span>user<span style="color:#666"> </span>|<span style="color:#666"> </span>author<span style="color:#666"> </span>|<span style="color:#666"> </span>moderator<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>UpdateComment<span style="color:#666"> </span>=<span style="color:#666"> </span>moderator<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>DeleteComment<span style="color:#666"> </span>=<span style="color:#666"> </span>moderator<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>UpdateUser<span style="color:#666"> </span>=<span style="color:#666"> </span>self<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>DeleteUser<span style="color:#666"> </span>=<span style="color:#666"> </span>self<span style="color:#666">
</span></span></span><span style="display:flex;"><span>)<span style="color:#666">
</span></span></span></code></pre></div><p>Each action has only the bits of every user type set to 1. For instance
<code>CreateArticle</code> is 2 (0010), <code>UpdateArticle</code> is 12 (1100) and <code>CreateComment</code>
is 7 (0111). We could now create a function which determines whether given
action is allowed by simply checking if they share at least one bit set to 1,
for which we&rsquo;re going to utilize the <a href="https://en.wikipedia.org/wiki/Bitwise_operation#AND" target="_blank">bitwise
AND</a>
 operator.</p>
<p>self is a bit tricky here, because it must be populated differently per-entity.
So we should make sure that both users, articles and comments have ID:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>User<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">struct</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>ID<span style="color:#666">   </span>ID<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Role<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">int</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Name<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">string</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#999;font-style:italic">// ...</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>Article<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">struct</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>ID<span style="color:#666">     </span>ID<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Author<span style="color:#666"> </span>ID<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Name<span style="color:#666">   </span><span style="color:#6ab825;font-weight:bold">string</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#999;font-style:italic">// ...</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">type</span><span style="color:#666"> </span>Comment<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">struct</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>ID<span style="color:#666">      </span>ID<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Author<span style="color:#666">  </span>ID<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>Article<span style="color:#666"> </span>ID<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#999;font-style:italic">// ...</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span></code></pre></div><p>The <code>ID</code> type here can be anything comparable (e.g. int, string, UUID).
Now the function in question is painfully obvious:</p>
<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">func</span><span style="color:#666"> </span><span style="color:#447fcf">Allowed</span>(u<span style="color:#666"> </span>User,<span style="color:#666"> </span>action<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">int</span>,<span style="color:#666"> </span>target<span style="color:#666"> </span>ID)<span style="color:#666"> </span><span style="color:#6ab825;font-weight:bold">bool</span><span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#6ab825;font-weight:bold">if</span><span style="color:#666"> </span>user.ID<span style="color:#666"> </span>==<span style="color:#666"> </span>target<span style="color:#666"> </span>{<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">		</span>u.Role<span style="color:#666"> </span>|=<span style="color:#666"> </span>self<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span>}<span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">
</span></span></span><span style="display:flex;"><span><span style="color:#666">	</span><span style="color:#6ab825;font-weight:bold">return</span><span style="color:#666"> </span>u.Role&amp;action<span style="color:#666"> </span>!=<span style="color:#666"> </span><span style="color:#3677a9">0</span><span style="color:#666">
</span></span></span><span style="display:flex;"><span>}<span style="color:#666">
</span></span></span></code></pre></div><p><code>target</code> is the ID of whatever is being acted upon or left with the zero value
for that type when not applicable (e.g. on any Create&hellip; action). In order to
check whether a user (u) can update given article (a) we call
<code>Allowed(u, UpdateArticle, a.Author)</code> and handle (e.g. return error) when the
result is false.</p>
<h2 id="however">However&hellip;</h2>
<p>You&rsquo;ve probably already figured out that this mimics the way Unix operating
systems encode filesystem access. I could think of a few pros to this approach:</p>
<ul>
<li>The check whether given action is allowed is trivial</li>
<li>Adding new user type is a single line of code. Each new user adds exactly one
bit to the mask, so until we go above 64, everything&rsquo;s fine</li>
<li>Permissions are obvious even to non-technical people</li>
<li>Same is applicable to pretty much any programming language I can think of</li>
</ul>
<p>However, this does not come without gotchas. Take that <code>self</code> which  is not
really a user type, but a made-up concept to allow everybody to edit <strong>some
of</strong> their own creations. We had to add it, because it&rsquo;s not a desired
capability for all entities. Comments for instance can NOT be edited, once
posted. Not only that, we had to add a new argument and logic to our little
pure function.</p>
<p>One could think of a few new requirements that would seem simple, yet require
more arguments, more checks and eventually a complete overhaul of this whole
idea. Technically speaking, even the Unix permissions turned out to
be not enough and modern Unix-like operating systems implement more complex
ACLs stored in the extended file attributes on top of it.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
