<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>cesarkina.com</title><link>https://cesarkina.com/</link><language>en-us</language><description>Blog: cesarkina.com</description><lastBuildDate>Tue, 19 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://cesarkina.com/rss.xml" rel="self" type="application/rss+xml"/><item><title>cktop - from /proc to gopsutil and back</title><link>https://cesarkina.com/blog/cktop-from-proc-to-gopsutil-and-back/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/cktop-from-proc-to-gopsutil-and-back/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>Notes on the rationale behind a recent decision I made for &lt;code>cktop&lt;/code> [1] (my cli tool to monitor machines CPU, memory and processes) to start reading stats from &lt;code>/proc&lt;/code> instead of using &lt;code>gopsutil&lt;/code>. The story is a full circle: the original version of the tool was already reading &lt;code>/proc&lt;/code> [2], then I switched to &lt;code>gopsutil&lt;/code>, and then I went back to &lt;code>/proc&lt;/code>. Here is why.&lt;/p>
&lt;h2 id="context">Context&lt;/h2>
&lt;p>I started to build &lt;code>cktop&lt;/code> because I wanted to:&lt;/p>
&lt;ol>
&lt;li>know what my machine (a PC running Debian) is running and stats about resources&lt;/li>
&lt;li>build a tool I can use on a daily basis, produce natural/organic feedback, friction to build cognitive muscles outside work&lt;/li>
&lt;li>be exposed to topics I am interested in: observability, OS, golang&lt;/li>
&lt;/ol>
&lt;h2 id="why-gopsutil">Why gopsutil&lt;/h2>
&lt;p>&lt;code>gopsutil&lt;/code> was my go-to library to get what I needed: stats CPU, memory and processes, and was great because it also supports other OS like Windows, so I could even test my binaries in my wife&amp;rsquo;s laptop, cool.&lt;/p>
&lt;h2 id="the-issue">The issue&lt;/h2>
&lt;p>Nothing wrong with &lt;code>gopsutil&lt;/code>, it does what it&amp;rsquo;s supposed to do. The problem was that I was not getting the feedback and learning I wanted, because &lt;code>gopsutil&lt;/code> abstracts all that knowledge for me: how &lt;code>/proc&lt;/code> is structured, what fields to read, how to parse them, how to interpret them. I destroyed my learning experience unintentionally. That is the main reason I decided to go back to read directly &lt;code>/proc&lt;/code>.&lt;/p>
&lt;h2 id="the-move">The move&lt;/h2>
&lt;p>Shortly after I realized I was not getting the knowledge I originally intended to, I decided to go back to read &lt;code>/proc&lt;/code> directly again [3]. A few things came up:&lt;/p>
&lt;ol>
&lt;li>Clean Architecture and FCIS (Functional Core, Imperative Shell) [4] paid off here. The domain layer defines three interfaces, &lt;code>MemoryReader&lt;/code>, &lt;code>ProcessReader&lt;/code>, and &lt;code>CPUReader&lt;/code>, and they know nothing about where the data actually comes from. Both the &lt;code>gopsutil&lt;/code> adapters and the new &lt;code>/proc&lt;/code> adapters implement those same interfaces. The only place in the codebase that knows which one to use is &lt;code>main.go&lt;/code>. So the full migration ended up being 3 lines changed there, just swapping the adapter names. The UI layer, the domain logic, the collector, none of that had to change. I could also leave the &lt;code>gopsutil&lt;/code> adapter in place for a while if I needed it. I decided to remove the dead code at the end.&lt;/li>
&lt;li>I dropped multi-OS support, so no more binaries on my wife&amp;rsquo;s laptop or my neighbor&amp;rsquo;s MacBook. But &lt;code>cktop&lt;/code> is a learning tool for me, and my interests are Linux, Go, and systems observability, so Linux-only aligns better with what I actually wanted to build.&lt;/li>
&lt;li>I just learned that &lt;code>/proc&lt;/code> is actually NOT a directory, it is defined as a &amp;ldquo;pseudo file-system&amp;rdquo; where we can find statistic data written by the Linux kernel: CPU, memory, processes [5]. Being a &amp;ldquo;pseudo file-system&amp;rdquo;, we can read files there as if they were real directories and files, that&amp;rsquo;s why &lt;code>cktop&lt;/code> can use plain &lt;code>os.Open&lt;/code> [6].&lt;/li>
&lt;li>I found things I didn&amp;rsquo;t know, things &lt;code>gopsutil&lt;/code> was handling silently. For example, I had no idea that CPU usage is not a single number you just read, it requires two consecutive reads and computing the difference between them. Or that memory &amp;ldquo;available&amp;rdquo; and memory &amp;ldquo;free&amp;rdquo; are not the same thing. Reading &lt;code>/proc&lt;/code> directly made all of that visible, and that&amp;rsquo;s exactly what I was looking for from the beginning.&lt;/li>
&lt;/ol>
&lt;h2 id="conclusions">Conclusions&lt;/h2>
&lt;p>Using &lt;code>gopsutil&lt;/code> saved me time getting features done. But it also blocked the learning I originally wanted, and I didn&amp;rsquo;t realize it until I was already deep in it. This time, learning &amp;gt; features.&lt;/p>
&lt;p>Also, because the data source was behind an interface, I could swap the whole implementation and the rest of the app didn&amp;rsquo;t even notice. That&amp;rsquo;s Clean Architecture and FCIS doing their job, not as a concept but as something practical that actually saved me time when I needed it.&lt;/p>
&lt;p>&lt;code>cktop&lt;/code> still is my personal side project I use to learn concepts about what&amp;rsquo;s interesting to me these days: Go + Observability + Systems + Linux. I have some more ideas to introduce to this tool soon, they relate to metrics and monitoring and how to persist stats, which is also huge for me.&lt;/p>
&lt;ul>
&lt;li>&lt;code>[1]&lt;/code>: &lt;a href="https://github.com/ckinan/cktop/">https://github.com/ckinan/cktop/&lt;/a>&lt;/li>
&lt;li>&lt;code>[2]&lt;/code>: &lt;a href="https://github.com/ckinan/cktop/commit/974e02cfa0d1d1b840af3b4fa31dcd375a11275a">https://github.com/ckinan/cktop/commit/974e02cfa0d1d1b840af3b4fa31dcd375a11275a&lt;/a>&lt;/li>
&lt;li>&lt;code>[3]&lt;/code>: &lt;a href="https://github.com/ckinan/cktop/commit/66d8d651d59da31a3ae4b4bb2ddcd84e08b34bb1">https://github.com/ckinan/cktop/commit/66d8d651d59da31a3ae4b4bb2ddcd84e08b34bb1&lt;/a>&lt;/li>
&lt;li>&lt;code>[4]&lt;/code>: &lt;a href="https://cesarkina.com/blog/clean-architecture-fcis/">https://cesarkina.com/blog/clean-architecture-fcis/&lt;/a>&lt;/li>
&lt;li>&lt;code>[5]&lt;/code>: &lt;a href="https://man7.org/linux/man-pages/man5/proc.5.html">https://man7.org/linux/man-pages/man5/proc.5.html&lt;/a>&lt;/li>
&lt;li>&lt;code>[6]&lt;/code>: &lt;a href="https://github.com/ckinan/cktop/blob/7e64ad620183d2e1fc9e1dde7f7f8fa573b35121/internal/adapters/proc/mem.go#L17">https://github.com/ckinan/cktop/blob/7e64ad620183d2e1fc9e1dde7f7f8fa573b35121/internal/adapters/proc/mem.go#L17&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Finding the right architecture for my CLI tool - Clean Architecture and FCIS</title><link>https://cesarkina.com/blog/clean-architecture-fcis/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/clean-architecture-fcis/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>While working on &lt;code>cktop&lt;/code> (my own &lt;code>htop&lt;/code> tool to monitor my machines) [1], I noticed some architectural friction while implementing features. This tool is just a CLI that shows process data (CPU, memory usage) that&amp;rsquo;s running on a machine. So it&amp;rsquo;s not an enterprise application that needs an ultra clever abstraction.&lt;/p>
&lt;p>However, very soon I ended up myself mixing &amp;ldquo;core&amp;rdquo; logic with UI logic, for example, logic to sort processes by PID, and how I present the sorted slice in terminal. That smelled really bad. So I decided to apply some form of Clean Architecture, where the domain code doesn&amp;rsquo;t know anything about concrete external dependencies. That alleviated some of the friction, boundaries started to be clearer. Yet, UI started to grow in lines of code, and boundaries started to be less clear again.&lt;/p>
&lt;p>Clean Architecture, in my opinion, for a project like this can feel mentally exhausting, like before adding a new logic or feature, I have to think about how it will fit best across multiple layers, dependency inversion, and all the nice words about it, while I like the concepts, it was just slowing me down, again, mentally.&lt;/p>
&lt;p>So I found another concept, which apparently isn&amp;rsquo;t new, it&amp;rsquo;s been there for more than a decade, but it is certainly new to-me. &amp;ldquo;Functional Core, Imperative Shell&amp;rdquo; [2]. Citing:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>A functional core should contain pure, testable business logic&lt;/strong>, which is free of side effects (such as I/O or external state mutation). It operates only on the data it is given.&lt;/p>
&lt;p>&lt;strong>An imperative shell is responsible for side effects&lt;/strong>, like database calls and sending emails. It uses the functions in your functional core to perform the business logic.&lt;/p>
&lt;/blockquote>
&lt;p>What I found useful to understand is that FCIS and Clean Architecture aren&amp;rsquo;t really alternatives, they operate at different levels. CA is about how you organize layers and manage dependency direction. FCIS is about whether functions inside those layers are pure or impure. You can use FCIS within a Clean Architecture.&lt;/p>
&lt;p>So that&amp;rsquo;s basically what I tried to follow within &lt;code>cktop&lt;/code>, extracted some &amp;ldquo;domain/core&amp;rdquo; logic from the UI to a pure function that can be easily tested and invoked from anywhere. It was easy to determine which part of the logic can be extracted and be &amp;ldquo;pure&amp;rdquo;, so the mental-heavy-friction I usually have with Clean Architecture wasn&amp;rsquo;t there. I am not saying FCIS is going to be always my go-to architectural decision over Clean Architecture, in fact, from what I liked, is that they can co-exist, but definitely a refreshing mental model to not overcomplicate things when shouldn&amp;rsquo;t. FCIS gave me a clear, compiler-enforceable boundary with no ceremony. For a project this size, that&amp;rsquo;s enough.&lt;/p>
&lt;p>Below is the thought process that I documented while I was trying to ease the friction in &lt;code>cktop&lt;/code> [3]:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/clean-architecture-fcis/images/0.png" alt="">&lt;/p>
&lt;p>And this is one of the &amp;ldquo;pure functions&amp;rdquo; I extracted from the UI code to the domain layer. No receivers, just domain inputs and outputs being processed.&lt;/p>
&lt;pre>&lt;code class="language-go"> // FilterProcesses returns the subset of procs whose fields contain query (case-insensitive).
// Returns procs unchanged if query is empty.
func FilterProcesses(procs []Process, query string) []Process {
if query == &amp;quot;&amp;quot; {
return procs
}
q := strings.ToLower(query)
var out []Process
for _, p := range procs {
if strings.Contains(strings.ToLower(fmt.Sprintf(&amp;quot;%d %d %s %s %d&amp;quot;, p.Pid, p.Ppid, p.Username, p.Cmdline, p.Rss)), q) {
out = append(out, p)
}
}
return out
}
&lt;/code>&lt;/pre>
&lt;p>Yes, this is just a &lt;code>strings.Contains&lt;/code> call. The point isn&amp;rsquo;t the complexity of the extracted function. It&amp;rsquo;s learning to recognize which code has no business knowing about the terminal, the bubbletea framework, or any side effect at all. That recognition is what FCIS gives you.&lt;/p>
&lt;ul>
&lt;li>&lt;code>[1]&lt;/code>: &lt;a href="https://github.com/ckinan/cktop">https://github.com/ckinan/cktop&lt;/a>&lt;/li>
&lt;li>&lt;code>[2]&lt;/code>: &lt;a href="https://testing.googleblog.com/2025/10/simplify-your-code-functional-core.html">https://testing.googleblog.com/2025/10/simplify-your-code-functional-core.html&lt;/a>&lt;/li>
&lt;li>&lt;code>[3]&lt;/code>: &lt;a href="https://github.com/ckinan/cktop/issues/2#issuecomment-4453212531">https://github.com/ckinan/cktop/issues/2#issuecomment-4453212531&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to show a clock in terminal prompt</title><link>https://cesarkina.com/blog/clock-terminal-prompt/</link><pubDate>Mon, 24 Jan 2022 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/clock-terminal-prompt/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>How to show a clock in your terminal prompt.&lt;/p>
&lt;h2 id="goal">Goal&lt;/h2>
&lt;p>In terminal, some times I want (and need) to see the exact time when I am running a command. So, two things to
accomplish:&lt;/p>
&lt;ul>
&lt;li>Show the current time in terminal, this should work as an actual clock moving. E.g. 13:32:54, then 13:32:55, and so
on.&lt;/li>
&lt;li>Take a snapshot of the exact time when a command was run. E.g. I ran this command at 13:32:55 exactly, so that I can
revisit my terminal and see when that happened.&lt;/li>
&lt;/ul>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>Avoid bloated plugins / solutions, since all I want to have is literally a clock in my terminal prompt, the simpler the
better, considering I run shell with &lt;a href="https://es.wikipedia.org/wiki/Zsh">zsh&lt;/a> on Ubuntu 20.04.3 LTS.&lt;/p>
&lt;p>After some research, found this in askubuntu.com: &lt;a href="https://askubuntu.com/a/360172">https://askubuntu.com/a/360172&lt;/a> . This answer is well explained, I just
adjusted some of the styles that worked for me.&lt;/p>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>This is the simplified version of the script in my &lt;code>.zshrc&lt;/code> file that should hopefully help my future me (and probably
others) to see what&amp;rsquo;s needed:&lt;/p>
&lt;pre>&lt;code class="language-bash"># &amp;quot;If the PROMPT_SUBST option is set, the prompt string is first subjected to parameter expansion, command substitution and arithmetic expansion.&amp;quot;
# See: https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html
setopt PROMPT_SUBST
# Clock format
# See: Same link above
PROMPT='%D{%H:%M:%S} $ '
# Set time out of 1 second
# See: https://zsh.sourceforge.io/Doc/Release/Parameters.html
TMOUT=1
# Executed every TMOUT seconds. 1 second in this example
TRAPALRM() {
# Reset the prompt, so that the clock acts like it's &amp;quot;moving&amp;quot;
# See: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#index-reset_002dprompt
zle reset-prompt
}
&lt;/code>&lt;/pre>
&lt;h2 id="results">Results&lt;/h2>
&lt;p>In terminal, clock should show like so:&lt;/p>
&lt;pre>&lt;code>13:49:00 $ date
Mon 24 Jan 2022 01:49:00 PM -05
13:49:04 $ date
Mon 24 Jan 2022 01:49:04 PM -05
&lt;/code>&lt;/pre>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html">https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#index-reset_002dprompt">https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#index-reset_002dprompt&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zsh.sourceforge.io/Doc/Release/Parameters.html">https://zsh.sourceforge.io/Doc/Release/Parameters.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cheat Sheet - Java</title><link>https://cesarkina.com/blog/cheat-sheet-java/</link><pubDate>Thu, 13 Jan 2022 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/cheat-sheet-java/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>My Cheat Sheet for: Java.&lt;/p>
&lt;p>Gathering quick references I used to search over and over again. Good to have them handy and supposed to (slowly) grow.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Which Functional Interface fits better for my use case? This answer from stackoverflow gives exactly what my brain can&amp;rsquo;t
retain and forces me to read this more than once: &lt;a href="https://stackoverflow.com/a/62434813">https://stackoverflow.com/a/62434813&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>How can I prettify this one line of java object data? : &lt;a href="https://codebeautify.org/javaviewer">https://codebeautify.org/javaviewer&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Before prettify&lt;/p>
&lt;pre>&lt;code>ClassA{id=1, name='My ClassA', classB=[ClassB{id=100, name='My ClassB (1)'}, ClassB{id=101, name='My ClassB (2)'}]}
&lt;/code>&lt;/pre>
&lt;p>After prettify&lt;/p>
&lt;pre>&lt;code>ClassA {
id = 1, name = 'My ClassA', classB = [ClassB {
id = 100, name = 'My ClassB (1)'
}, ClassB {
id = 101, name = 'My ClassB (2)'
}]
}
&lt;/code>&lt;/pre>
&lt;hr style="height: 1px; background-color: black; border: none;">
&lt;p>See other cheatsheets &lt;a href="https://cesarkina.com/notes#cheatsheets">here&lt;/a>&lt;/p></description></item><item><title>Connect to MySQL via terminal with mycli</title><link>https://cesarkina.com/blog/mysql-client-mycli/</link><pubDate>Fri, 04 Jun 2021 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/mysql-client-mycli/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>Quick note on how to connect to MySQL via terminal.&lt;/p>
&lt;p>First option, I went to MySQL docs, found &lt;code>mysql&lt;/code> command can be installed from here: &lt;a href="https://dev.mysql.com/downloads/mysql/">https://dev.mysql.com/downloads/mysql/&lt;/a>.
The only &amp;ldquo;problem&amp;rdquo; with that is it would also install a MySQL Server in my machine, and I just wanted to have the client.&lt;/p>
&lt;p>Found &lt;a href="https://www.mycli.net/">mycli&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>A command line client for MySQL that can do auto-completion and syntax highlighting.&lt;/p>
&lt;/blockquote>
&lt;p>Followed the installation instructions, and.. connected!&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/mysql-client-mycli/images/1.png" alt="">&lt;/p>
&lt;p>Big thanks to the people involved in this excellent cli!&lt;/p>
&lt;h2 id="notes">Notes&lt;/h2>
&lt;p>Tested on:&lt;/p>
&lt;ul>
&lt;li>Ubuntu 20.04.2 LTS&lt;/li>
&lt;li>Python 3.8.5&lt;/li>
&lt;/ul></description></item><item><title>How to set timezone in Intellij IDEA</title><link>https://cesarkina.com/blog/intellij-timezone/</link><pubDate>Fri, 28 May 2021 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/intellij-timezone/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>Quick note on how to set timezone in Intellij IDEA.&lt;/p>
&lt;h2 id="steps">Steps&lt;/h2>
&lt;ul>
&lt;li>Step 1: Create a simple java project (&lt;code>main()&lt;/code> method to just print the current date-time for testing)&lt;/li>
&lt;li>Step 2: Set timezone in Intellij (will set timezone via JVM options)&lt;/li>
&lt;/ul>
&lt;h2 id="step-1-create-a-simple-java-project">Step 1: Create a simple java project&lt;/h2>
&lt;ul>
&lt;li>Create a Java project with only one single java class called &lt;code>App.java&lt;/code>.&lt;/li>
&lt;li>Create its method main to be executable&lt;/li>
&lt;li>Print the current date and time in console&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-java">import java.util.Date;
public class App {
public static void main(String[] args) {
System.out.println(new Date());
}
}
&lt;/code>&lt;/pre>
&lt;p>Run the app (right click on the class &amp;gt; Run App.main), current date and time reflects my timezone in the console (Peru in my case):&lt;/p>
&lt;pre>&lt;code>Fri May 28 16:38:08 PET 2021
&lt;/code>&lt;/pre>
&lt;h2 id="step-2-set-timezone-in-intellij">Step 2: Set timezone in Intellij&lt;/h2>
&lt;ul>
&lt;li>Open Run/Debug Configurations and click on Add VM options&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cesarkina.com/blog/intellij-timezone/images/1.png" alt="">&lt;/p>
&lt;ul>
&lt;li>Under VM options, type the timezone you want. Example: &lt;code>-Duser.timezone=&amp;quot;UTC&amp;quot;&lt;/code> if you want UTC.&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cesarkina.com/blog/intellij-timezone/images/2.png" alt="">&lt;/p>
&lt;ul>
&lt;li>Run the application again, current date and time should now reflect UTC in the console:&lt;/li>
&lt;/ul>
&lt;pre>&lt;code>Fri May 28 21:43:39 UTC 2021
&lt;/code>&lt;/pre>
&lt;h2 id="notes">Notes&lt;/h2>
&lt;p>Tested with:&lt;/p>
&lt;ul>
&lt;li>Intellij IDEA v2021.1.1&lt;/li>
&lt;li>Java 8&lt;/li>
&lt;/ul></description></item><item><title>Cheat Sheet - Python</title><link>https://cesarkina.com/blog/cheat-sheet-python/</link><pubDate>Sat, 22 May 2021 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/cheat-sheet-python/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>My Cheat Sheet for: Python.&lt;/p>
&lt;p>Gathering quick references I used to search over and over again. Good to have them handy and supposed to (slowly) grow.&lt;/p>
&lt;p>Create virtual environment with name &lt;code>venv&lt;/code>&lt;/p>
&lt;pre>&lt;code>python3 -m venv venv
&lt;/code>&lt;/pre>
&lt;p>Activate virtual environment &lt;code>venv&lt;/code>&lt;/p>
&lt;pre>&lt;code>source venv/bin/activate
&lt;/code>&lt;/pre>
&lt;p>Exit virtual environment&lt;/p>
&lt;pre>&lt;code>deactivate
&lt;/code>&lt;/pre>
&lt;p>Save packages to requirements.txt&lt;/p>
&lt;pre>&lt;code>pip freeze &amp;gt; requirements.txt
&lt;/code>&lt;/pre>
&lt;p>Install packages from requirements.txt&lt;/p>
&lt;pre>&lt;code>pip install -r requirements.txt
&lt;/code>&lt;/pre>
&lt;p>Clean ALL packages. Credits: &lt;a href="https://stackoverflow.com/questions/11248073/what-is-the-easiest-way-to-remove-all-packages-installed-by-pip">https://stackoverflow.com/questions/11248073/what-is-the-easiest-way-to-remove-all-packages-installed-by-pip&lt;/a>&lt;/p>
&lt;pre>&lt;code>pip freeze | xargs pip uninstall -y
&lt;/code>&lt;/pre>
&lt;p>&lt;a href="https://pypi.org/project/pipdeptree/">pipdeptree&lt;/a>: Useful python command to see packages in dependency tree format&lt;/p>
&lt;pre>&lt;code># Command:
$ pipdeptree
# Output:
commonmark==0.9.1
Jinja2==2.11.3
- MarkupSafe [required: &amp;gt;=0.23, installed: 1.1.1]
pipdeptree==2.0.0
- pip [required: &amp;gt;=6.0.0, installed: 21.0.1]
python-frontmatter==0.5.0
- PyYAML [required: Any, installed: 5.4]
- six [required: Any, installed: 1.15.0]
setuptools==54.2.0
&lt;/code>&lt;/pre>
&lt;hr style="height: 1px; background-color: black; border: none;">
&lt;p>See other cheatsheets &lt;a href="https://cesarkina.com/notes#cheatsheets">here&lt;/a>&lt;/p></description></item><item><title>Tinylog - Simple example with Rolling File Writer</title><link>https://cesarkina.com/blog/tinylog-simple-example-rolling-file-writer/</link><pubDate>Tue, 26 Jan 2021 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/tinylog-simple-example-rolling-file-writer/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>This is a simple example of how to configure a Rolling File Writer with &lt;a href="https://tinylog.org/v2/">Tinylog&lt;/a>.&lt;/p>
&lt;h2 id="steps">Steps&lt;/h2>
&lt;ul>
&lt;li>Create a java application that simply writes a log using Tinylog (our &amp;ldquo;Hello World&amp;rdquo;)&lt;/li>
&lt;li>Setup a basic Rolling File Writer&lt;/li>
&lt;li>Customize our Rolling File Writer&lt;/li>
&lt;li>Test every significant change during the process so that we can understand each piece&lt;/li>
&lt;/ul>
&lt;h2 id="requirements">Requirements&lt;/h2>
&lt;ul>
&lt;li>Java 6 or higher&lt;/li>
&lt;li>Maven&lt;/li>
&lt;li>IDE of your preference (I&amp;rsquo;ll use Intellij IDEA)&lt;/li>
&lt;/ul>
&lt;h2 id="create-a-java-application">Create a java application&lt;/h2>
&lt;p>Create the &lt;code>pom.xml&lt;/code> and add the two dependencies required to work with Tinylog.&lt;/p>
&lt;pre>&lt;code class="language-xml">&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;project xmlns=&amp;quot;http://maven.apache.org/POM/4.0.0&amp;quot;
xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;
xsi:schemaLocation=&amp;quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&amp;quot;&amp;gt;
&amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
&amp;lt;groupId&amp;gt;com.ckinan&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;tinylog&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;lt;properties&amp;gt;
&amp;lt;java.version&amp;gt;1.8&amp;lt;/java.version&amp;gt;
&amp;lt;maven.compiler.source&amp;gt;1.8&amp;lt;/maven.compiler.source&amp;gt;
&amp;lt;maven.compiler.target&amp;gt;1.8&amp;lt;/maven.compiler.target&amp;gt;
&amp;lt;/properties&amp;gt;
&amp;lt;dependencies&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.tinylog&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;tinylog-api&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;2.2.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.tinylog&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;tinylog-impl&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;2.2.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>Create a java class, I&amp;rsquo;ll name it &lt;code>App.java&lt;/code> with a main method that will just log a &lt;code>Hello World&lt;/code> (for now).&lt;/p>
&lt;pre>&lt;code class="language-java">package com.ckinan;
import org.tinylog.Logger;
public class App {
public static void main(String[] args) {
Logger.info(&amp;quot;Hello world&amp;quot;);
}
}
&lt;/code>&lt;/pre>
&lt;p>Run the App class, you should see this in your console:&lt;/p>
&lt;pre>&lt;code>2021-01-26 17:37:28 [main] com.ckinan.App.main()
INFO: Hello world
&lt;/code>&lt;/pre>
&lt;p>Ok, so far we just wrote almost the same setup from the &lt;a href="https://tinylog.org/v2/getting-started/">Getting Started&lt;/a>
documentation. Now, let&amp;rsquo;s configure the Rolling File Writer.&lt;/p>
&lt;h2 id="setup-a-rolling-file-writer">Setup a Rolling File Writer&lt;/h2>
&lt;p>Create a file &lt;code>tinylog.properties&lt;/code> in &lt;code>src/main/resources&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-properties">writer = rolling file
writer.file = log/app_{count}.log
&lt;/code>&lt;/pre>
&lt;p>Run the App class, you should see:&lt;/p>
&lt;ol>
&lt;li>A new folder created by Tinylog in the root path of your java application &lt;code>./log&lt;/code>&lt;/li>
&lt;li>A log file created by Tinylog located in &lt;code>./log/app_0.log&lt;/code>&lt;/li>
&lt;li>The content of the log file should be our &lt;code>Hello World&lt;/code>:&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>2021-01-26 17:59:44 [main] com.ckinan.App.main()
INFO: Hello world
&lt;/code>&lt;/pre>
&lt;ol start="4">
&lt;li>If you run the java application a second time, a new log file will be created, this time with name &lt;code>./log/app_1.log&lt;/code>.
That&amp;rsquo;s expected because with our initial configuration in &lt;code>tinylog.properties&lt;/code> it&amp;rsquo;s taking default values about how new
log entries will be written in the log files. However, we can customize the way these log files are created, their
format, and policies. We will do that in a bit.&lt;/li>
&lt;/ol>
&lt;p>Ok, so far we created the Rolling File Writer with just the required properties in it. Now, let&amp;rsquo;s customize our writer
so that we can see a bit more what we can do with it.&lt;/p>
&lt;h2 id="customize-our-rolling-file-writer">Customize our Rolling File Writer&lt;/h2>
&lt;p>Let&amp;rsquo;s we have the following needs:&lt;/p>
&lt;ul>
&lt;li>Only consider logging with level INFO&lt;/li>
&lt;li>Custom format date like &lt;code>2021-01-26 20:31:57.729&lt;/code>&lt;/li>
&lt;li>Class name and line number specifying where exactly the logging came from&lt;/li>
&lt;li>The actual message log&lt;/li>
&lt;li>The process ID of the application&lt;/li>
&lt;li>The thread where the logging came from&lt;/li>
&lt;li>Each log file (being rotated by Tinylog) should have maximum 50KB of size&lt;/li>
&lt;li>Rotate to maximum 5 log files&lt;/li>
&lt;/ul>
&lt;p>All the above information (and more) can be achieved by the logging configuration that Tinylog provides. For more
details about this configuration, go to &lt;a href="https://tinylog.org/v2/configuration">https://tinylog.org/v2/configuration&lt;/a>.&lt;/p>
&lt;p>In order to meet the said criteria, let&amp;rsquo;s update our &lt;code>tinylog.properties&lt;/code> file:&lt;/p>
&lt;pre>&lt;code class="language-properties">writer = rolling file
writer.level = info
writer.format = {date: yyyy-MM-dd HH:mm:ss.SSS} {pid}/{thread-id} {class}:{line} {level}: {message}
writer.file = log/app_{count}.log
writer.charset = UTF-8
writer.policies = size: 50KB
writer.backups = 5
writer.append = true
&lt;/code>&lt;/pre>
&lt;p>Also, let&amp;rsquo;s update our java application to print more entries in the log, so that the log files can be rotated:&lt;/p>
&lt;pre>&lt;code class="language-java">package com.ckinan;
import org.tinylog.Logger;
public class App {
public static void main(String[] args) {
for(int i=0; i&amp;lt;6000; i++) {
Logger.info(i);
}
}
}
&lt;/code>&lt;/pre>
&lt;p>Run the App class, you should see:&lt;/p>
&lt;ol>
&lt;li>Five files with maximum 50KB of size per each. We intentionally forced to create six files so that we can see that
&lt;code>app_0.log&lt;/code> and &lt;code>app_1.log&lt;/code> log files where removed given our configuration to have maximum 5 log files as backup.&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>$ ls -l ./log
total 512
drwxr-xr-x 7 ckinan staff 224B Jan 26 20:41 .
drwxr-xr-x 9 ckinan staff 288B Jan 26 20:41 ..
-rw-r--r-- 1 ckinan staff 50K Jan 26 20:41 app_2.log
-rw-r--r-- 1 ckinan staff 50K Jan 26 20:41 app_3.log
-rw-r--r-- 1 ckinan staff 50K Jan 26 20:41 app_4.log
-rw-r--r-- 1 ckinan staff 50K Jan 26 20:41 app_5.log
-rw-r--r-- 1 ckinan staff 45K Jan 26 20:41 app_6.log
&lt;/code>&lt;/pre>
&lt;ol start="2">
&lt;li>Log entries should show our custom date format, the process id, thread name, class name and line number. All that
according to the format we specified in &lt;code>tinylog.properties&lt;/code> under &lt;code>writer.format&lt;/code>.&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>2021-01-26 20:41:45.708 2990/1 com.ckinan.App:9 INFO: 1754
&lt;/code>&lt;/pre>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>When it comes to initial setup, I found Tinylog very easy to follow. Their &lt;a href="https://tinylog.org/v2/getting-started/">Getting Started&lt;/a>
guide shows all what I needed (dependencies, requirements and the &amp;ldquo;Hello World&amp;rdquo;). Later, when I had to setup the log
files rotation, the documentation was also pretty clear on what I needed. I found this logging framework while working
on a side project and I feel it was an excellent choice because of its simplicity to cover all my needs regarding what
I could do with the Rolling File Writer.&lt;/p>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>Just go to Tinylog site: &lt;a href="https://tinylog.org/v2/">https://tinylog.org/v2/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>My own static site generator</title><link>https://cesarkina.com/blog/my-own-static-site-generator/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/my-own-static-site-generator/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>I am moving from using Gatsby to start generating this site with my own script generator.&lt;/p>
&lt;h2 id="the-problem">The problem&lt;/h2>
&lt;p>I&amp;rsquo;d say, there is not an actual problem with Gatsby. In fact, everything I&amp;rsquo;ve done so far with Gatsby went pretty well.
I just want to learn what it takes to do it from scratch, being my site a very simple blog without any complex data
states or plugins everywhere.&lt;/p>
&lt;h2 id="scope">Scope&lt;/h2>
&lt;p>This new generator should:&lt;/p>
&lt;ul>
&lt;li>Create pages based on templates and layouts&lt;/li>
&lt;li>Support markdown, because I want to keep the current format of my articles&lt;/li>
&lt;li>Support frontmatter, because again, all the metadata of my articles should not change at all&lt;/li>
&lt;/ul>
&lt;h2 id="out-of-the-scope">Out of the scope&lt;/h2>
&lt;p>I don&amp;rsquo;t really want to complicate myself with javascript. So, all my pages will be generated in plain HTML files.&lt;/p>
&lt;h2 id="the-tool">The tool&lt;/h2>
&lt;p>I just wrote a very simple python script that uses:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://pypi.org/project/Jinja2/">jinja2&lt;/a>: To generate my pages with templates&lt;/li>
&lt;li>&lt;del>&lt;a href="https://pypi.org/project/commonmark/">commonmark&lt;/a>&lt;/del> &lt;a href="https://pypi.org/project/mistune/">mistune&lt;/a>: To support markdown for my pre-existing (and new) articles&lt;/li>
&lt;li>&lt;a href="https://pypi.org/project/frontmatter/">frontmatter&lt;/a>: To support frontmatter and read the metadata of my articles&lt;/li>
&lt;/ul>
&lt;p>The logic is actually simple, it just reads a directory (&lt;code>./blog&lt;/code>) which contains all the markdown files. Then iterates
them to render the list of posts in the main page. After that generates the About page and all the actual articles. All
that is done by using the above libraries. All templates are read from a second folder &lt;code>./resources&lt;/code>. Finally, the
static files are created in the &lt;code>./public&lt;/code>, which is overwritten everytime the python script runs.&lt;/p>
&lt;h2 id="local-testing">Local testing&lt;/h2>
&lt;p>For local environment, and since it is all static files, I&amp;rsquo;m using a Chrome extension called &lt;a href="https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb">Web Server for Chrome&lt;/a>
which is simple to use and accomplish my use case. I just need to locate the &lt;code>./public&lt;/code> folder and open
&lt;a href="http://127.0.0.1:8887">http://127.0.0.1:8887&lt;/a> in my Browser. It also has the ability to open the site in your local
network, so that I can test it in my mobile device.&lt;/p>
&lt;h2 id="deploy-in-production">Deploy in &amp;ldquo;production&amp;rdquo;&lt;/h2>
&lt;p>EDIT: On June 2021, I moved this site from Netlify to Firebase.&lt;/p>
&lt;p>&lt;del>Since I&amp;rsquo;m using Netlify to host my site, there are few things I had to do to have Netlify using my brand new static site
generator:&lt;/del>&lt;/p>
&lt;p>&lt;del>1) &lt;code>runtime.txt&lt;/code>: Needed to specify the python version that should be used to run my script. In my case I set it &lt;code>3.7&lt;/code>.&lt;/del>
&lt;del>2) &lt;code>netlify.toml&lt;/code>: Included a rule to redirect all &amp;ldquo;not found&amp;rdquo; resources to a custom &lt;code>404.html&lt;/code> page.&lt;/del>
&lt;del>3) Changed the &amp;ldquo;Build command&amp;rdquo; to have &lt;code>python generate.py&lt;/code>, so that Netlify will ran that command to actually build the
site per my requirement.&lt;/del>
&lt;del>4) Changed the &amp;ldquo;Publish directory&amp;rdquo; to be &lt;code>./public&lt;/code>, since I am giving instructions to the python script to generate all
static files to that folder.&lt;/del>&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>I know I am, kind of, reinventing the wheel. But it&amp;rsquo;s somewhat satisfying to see exactly what I expect my page to have:
Html files, a single css based on the script to generate the site. Nothing really fancy but meets my (very, very simple)
needs. I think it can work for some time.&lt;/p></description></item><item><title>email-reporter</title><link>https://cesarkina.com/blog/email-reporter/</link><pubDate>Fri, 04 Dec 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/email-reporter/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>I developed a java application that reads emails, parses the information and stores it into a data source. It&amp;rsquo;s &lt;a href="https://github.com/ckinan/email-reporter">email-reporter&lt;/a>.&lt;/p>
&lt;p>Currently, it implements:&lt;/p>
&lt;ul>
&lt;li>Gmail: Queries emails by a given label and dates&lt;/li>
&lt;li>Google Sheets: Reads and writes entries of what is parsed in the emails&lt;/li>
&lt;li>Spring Task Scheduler: Ability to schedule a task to read emails and generate reports by a given cron setup&lt;/li>
&lt;/ul>
&lt;p>I wanted to have a sort of plain data of some services I usually consume and get receipts via email. E.g. Uber. I couldn&amp;rsquo;t find an easy way to export my trips&amp;rsquo; data like the costs, discounts, and all the other fields that I saw interesting to keep track and that I have available in those email messages. Plus, I thought it could be fun to code something like this, and it was.&lt;/p></description></item><item><title>XPath selector on HTML with Java</title><link>https://cesarkina.com/blog/xpath-selector-on-html-java/</link><pubDate>Wed, 30 Sep 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/xpath-selector-on-html-java/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>I wanted to explore how easy is to extract elements from HTML files or directly from website URLs programatically using Java. I found a third party library that gave me the desired results without much difficulties.&lt;/p>
&lt;p>It&amp;rsquo;s XSoup.&lt;/p>
&lt;ul>
&lt;li>Github Repo: &lt;a href="https://github.com/code4craft/xsoup">https://github.com/code4craft/xsoup&lt;/a>&lt;/li>
&lt;li>Maven Repo: &lt;a href="https://mvnrepository.com/artifact/us.codecraft/xsoup">https://mvnrepository.com/artifact/us.codecraft/xsoup&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>With XPath expressions it is able to select the elements within the HTML using &lt;a href="https://jsoup.org/">Jsoup&lt;/a> as HTML parser.&lt;/p>
&lt;p>In the example below, it extracts the posts of this website using the URL (&lt;a href="https://cesarkina.com">https://cesarkina.com&lt;/a>) , or reading the html of that exact url but downloaded into the repository.&lt;/p>
&lt;h2 id="the-code">The code&lt;/h2>
&lt;p>Repo: &lt;a href="https://github.com/ckinan/learning/tree/main/java/parse-html">https://github.com/ckinan/learning/tree/main/java/parse-html&lt;/a>&lt;/p>
&lt;p>We got:&lt;/p>
&lt;ul>
&lt;li>One single file &lt;code>App.java&lt;/code> to place our logic&lt;/li>
&lt;li>One single dependency in our &lt;code>pom.xml&lt;/code> file: XSoup&lt;/li>
&lt;/ul>
&lt;h3 id="appjava">App.java&lt;/h3>
&lt;p>I tried to explain the code with the docstrings and comments there to point out what the lines and methods are supposed to do.&lt;/p>
&lt;pre>&lt;code class="language-java">package com.ckinan;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import us.codecraft.xsoup.Xsoup;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Code snippet to find elements within a html (from file and url) using XPath.
*
* It uses a third party library called XSoup. Links:
* - mvn repository: https://mvnrepository.com/artifact/us.codecraft/xsoup
* - github repository: https://github.com/code4craft/xsoup
*
* This example reads a list of blog posts using a fixed XPath expression and prints the results in the console.
* */
public class App {
public static final String XPATH_BLOG_POST = &amp;quot;//div[contains(@class, 'leading-snug')]&amp;quot;;
/**
* The method main of the program. It starts and orchestrates the process.
* It gets instances of Documents from specific sources and sends them to read specific elements
* by xpath expression.
*
* It retrieve documents from:
* - Files (by specifying the full path)
* - URL (of the website to be parsed)
*
* @param args
* Arguments of our application. Won't be used at all
* @throws java.io.IOException
*/
public static void main(String[] args) throws IOException {
// Calling the static methods that will get an instance of the HTML document and then evaluate
// the XPath expression to get a list of Blog Posts. First with a HTML file, then do the same with the website
// URL directly.
Document doc1 = App.getDocumentFromFile(&amp;quot;src/main/resources/blog.html&amp;quot;);
List&amp;lt;String&amp;gt; postsFromFile = App.readDocument(doc1, App.XPATH_BLOG_POST);
System.out.println(postsFromFile);
Document doc2 = App.getDocumentFromUrl(&amp;quot;https://cesarkina.com/blog&amp;quot;);
List&amp;lt;String&amp;gt; postsFromUrl = App.readDocument(doc2, App.XPATH_BLOG_POST);
System.out.println(postsFromUrl);
}
/**
* Read the content file of the given full path using &amp;quot;collect&amp;quot; from Stream Java 8+.
*
* Refs:
* - https://docs.oracle.com/javase/8/docs/api/java/nio/file/Paths.html#get-java.lang.String-java.lang.String...-
* - https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#lines-java.nio.file.Path-
* - https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.stream.Collector-
*
* Then it uses Jsoup to parse the content file into a Document instance.
*
* @param fullPath
* The full path of the file to be read and parsed using Jsoup.
* @throws java.io.IOException
*
* @return The parsed document which contains all the elements of the HTML file
*/
public static Document getDocumentFromFile(String fullPath) throws IOException {
System.out.println(&amp;quot;Reading document from file: &amp;quot; + fullPath);
// Read the content file using Java 8+ Streams.
String html = Files.lines(Paths.get(fullPath)).collect(Collectors.joining(System.lineSeparator()));
return Jsoup.parse(html);
}
/**
* Read the content of the given website url using Jsoup
*
* @param url
* The URL of the website HTML to be read and parsed using Jsoup.
*
* @throws java.io.IOException
*
* @return The parsed document which contains all the elements of the HTML file
*/
public static Document getDocumentFromUrl(String url) throws IOException {
System.out.println(&amp;quot;Reading document from url: &amp;quot; + url);
return Jsoup.connect(url).get();
}
/**
* Evaluates the given XPath expression on the given Document which is limited to assume that it's a list of
* Elements right now. Returns results of the XPath evaluation.
*
* @param doc
* The URL of the website HTML to be read and parsed using Jsoup.
*
* @param xpath
* The URL of the website HTML to be read and parsed using Jsoup.
*
* @throws java.io.IOException
*
* @return The list of string values resulted from the XPath expression.
*/
public static List&amp;lt;String&amp;gt; readDocument(Document doc, String xpath) {
List&amp;lt;String&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
// It first extract the Element instances of the html content using the given Document and the XPath expression
// Note: Xsoup uses Jsoup as the HTML parser. Xsoup &amp;quot;evaluates&amp;quot; a Document which is an instance variable created
// by Jsoup
List&amp;lt;Element&amp;gt; elements =
Xsoup.compile(xpath).evaluate(doc).getElements();
for(Element e: elements) {
// Each Element object has multiple properties available to be extracted, for example the HTML tag name.
// For this code snipped, we are interested into extract the text of the element.
// Example: &amp;lt;p&amp;gt;Hello world&amp;lt;/p&amp;gt; -&amp;gt; e.text() would return &amp;quot;Hello world&amp;quot;
result.add(e.text());
}
return result;
}
}
&lt;/code>&lt;/pre>
&lt;h3 id="pomxml-dependences-section-only">pom.xml (dependences section only)&lt;/h3>
&lt;pre>&lt;code class="language-xml">&amp;lt;dependencies&amp;gt;
&amp;lt;!-- https://mvnrepository.com/artifact/us.codecraft/xsoup --&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;us.codecraft&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;xsoup&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;0.3.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&lt;/code>&lt;/pre>
&lt;h2 id="testing">Testing&lt;/h2>
&lt;p>Run the &lt;code>App.java&lt;/code> class, and it should get this printed in the Console:&lt;/p>
&lt;pre>&lt;code>Reading document from file: src/main/resources/blog.html
[Sat 22:Remote Debug Spring Boot App in a Docker Container using IntelliJ IDEA, Thu 13:Cheat Sheet - Git Commands, Mon 20:Spring Security - Credentials from JSON request, Fri 03:Spring Security - Filter Chain, Sun 28:Spring Security - Form Login &amp;amp; Logout, Wed 17:KnockoutJS - Register Component, Mon 01:Spring Security - Getting Started, Sun 17:Tailwind CSS - First Steps, Fri 24:Project Github Pull Request Viewer, Fri 13:Github API - OAuth tokens for apps, Sat 07:Netlify Functions, Fri 07:Github API - First Steps, Sun 05:Auth0 - Simple Example with Vanilla JS, Tue 08:Intro]
Reading document from url: https://cesarkina.com/blog
[Sat 22:Remote Debug Spring Boot App in a Docker Container using IntelliJ IDEA, Thu 13:Cheat Sheet - Git Commands, Mon 20:Spring Security - Credentials from JSON request, Fri 03:Spring Security - Filter Chain, Sun 28:Spring Security - Form Login &amp;amp; Logout, Wed 17:KnockoutJS - Register Component, Mon 01:Spring Security - Getting Started, Sun 17:Tailwind CSS - First Steps, Fri 24:Project Github Pull Request Viewer, Fri 13:Github API - OAuth tokens for apps, Sat 07:Netlify Functions, Fri 07:Github API - First Steps, Sun 05:Auth0 - Simple Example with Vanilla JS, Tue 08:Intro]
&lt;/code>&lt;/pre>
&lt;h2 id="tooling">Tooling&lt;/h2>
&lt;p>Something uselful to &amp;ldquo;test&amp;rdquo; the XPath expression to see if it&amp;rsquo;s a valid one or if will work, it is this chrome extension: &lt;a href="https://chrome.google.com/webstore/detail/xpath-helper/hgimnogjllphhhkhlmebbmlgjoejdpjl">XPath Helper&lt;/a>. For the above example I introduced the XPath expression &lt;code>//div[contains(@class, 'leading-snug')]&lt;/code> and ensure the results made sense.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/xpath-selector-on-html-java/images/0.png" alt="">&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>I needed an XPath selector, and Xsoup worked well and without a lot of boilerplate, just like what I was looking for. So thanks Xsoup!&lt;/p>
&lt;h2 id="links">Links:&lt;/h2>
&lt;ul>
&lt;li>XSoup: &lt;a href="https://github.com/code4craft/xsoup">https://github.com/code4craft/xsoup&lt;/a>&lt;/li>
&lt;li>Jsoup: &lt;a href="https://jsoup.org/">https://jsoup.org/&lt;/a>&lt;/li>
&lt;li>Repo of the code snippet: &lt;a href="https://github.com/ckinan/learning/tree/main/java/parse-html">https://github.com/ckinan/learning/tree/main/java/parse-html&lt;/a>&lt;/li>
&lt;li>This tutorial helped me to understand the basics of XPath: &lt;a href="https://www.w3schools.com/xml/xpath_syntax.asp">https://www.w3schools.com/xml/xpath_syntax.asp&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Remote Debug Spring Boot App in a Docker Container using IntelliJ IDEA</title><link>https://cesarkina.com/blog/remote-debug-spring-boot-docker-intellij/</link><pubDate>Sat, 22 Aug 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/remote-debug-spring-boot-docker-intellij/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>I had to setup a local environment for a Spring Boot application running in a Docker container. Naturally, I needed to find a way to debug my app, which is the step I want to explain here.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;ul>
&lt;li>IntelliJ IDEA&lt;/li>
&lt;li>Docker&lt;/li>
&lt;/ul>
&lt;h2 id="scope">Scope&lt;/h2>
&lt;ul>
&lt;li>We will have only one (GET) endpoint that receives a &amp;ldquo;value&amp;rdquo; as string parameter and return that back to the client.&lt;/li>
&lt;li>We want to put a breakpoint in the controller to demonstrate the debug configuration is working properly.&lt;/li>
&lt;li>We don&amp;rsquo;t want more logic in our code as the goal is to get our debug config ready in this local/docker/springboot environment.&lt;/li>
&lt;/ul>
&lt;h2 id="create-the-project">Create the project&lt;/h2>
&lt;p>Generate and &lt;a href="https://start.spring.io/#!type=maven-project&amp;amp;language=java&amp;amp;platformVersion=2.3.3.RELEASE&amp;amp;packaging=jar&amp;amp;jvmVersion=14&amp;amp;groupId=com.ckinan&amp;amp;artifactId=demo&amp;amp;name=demo&amp;amp;description=Demo%20project%20for%20Spring%20Boot&amp;amp;packageName=com.ckinan.demo&amp;amp;dependencies=web">Download&lt;/a> a Spring Boot project with only &amp;ldquo;Spring Web&amp;rdquo; as a dependency.&lt;/p>
&lt;p>Open the project in IntelliJ.&lt;/p>
&lt;h2 id="create-the-resource-controller">Create the resource controller&lt;/h2>
&lt;p>Open the &lt;code>DemoApplication&lt;/code> class and create a resource controller, in this example mapped with alias &lt;code>/echo&lt;/code>, that is simply going to return the value from the request.&lt;/p>
&lt;pre>&lt;code class="language-java">package com.ckinan.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class DemoApplication {
@GetMapping(&amp;quot;/echo&amp;quot;)
public String resource(@RequestParam(value = &amp;quot;value&amp;quot;, defaultValue = &amp;quot;insert a 'value' query param&amp;quot;) String value) {
return &amp;quot;echo: &amp;quot; + value;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
&lt;/code>&lt;/pre>
&lt;h2 id="package-your-app">Package your app&lt;/h2>
&lt;p>Execute the following command manually to get the &lt;code>jar&lt;/code> file of your project generated using maven.&lt;/p>
&lt;pre>&lt;code>./mvnw package
&lt;/code>&lt;/pre>
&lt;p>You should have the &lt;code>jar&lt;/code> file created in the &lt;code>./target&lt;/code> folder. In this example, it should be under the name &lt;code>./target/demo-0.0.1-SNAPSHOT.jar&lt;/code>&lt;/p>
&lt;h2 id="create-the-dockerfile">Create the Dockerfile&lt;/h2>
&lt;p>Create a &lt;code>Dockerfile&lt;/code> in the root of the project.&lt;/p>
&lt;pre>&lt;code class="language-docker">FROM openjdk:14-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT [&amp;quot;java&amp;quot;,&amp;quot;-jar&amp;quot;,&amp;quot;/app.jar&amp;quot;]
&lt;/code>&lt;/pre>
&lt;p>We are giving the instruction to Docker to build an image from &lt;code>openjdk:14-jdk-alpine&lt;/code> and copy our recently created &lt;code>jar&lt;/code> file that will be used in our container.&lt;/p>
&lt;h2 id="build-the-image">Build the image&lt;/h2>
&lt;p>Run the &lt;code>docker build&lt;/code> command to create the Docker Image.&lt;/p>
&lt;pre>&lt;code>docker build -t ckina/demo-spring-boot-docker-debug .
&lt;/code>&lt;/pre>
&lt;p>The image will be created with the repository name &lt;code>ckina/demo-spring-boot-docker-debug&lt;/code>, change that if you want.&lt;/p>
&lt;h2 id="remote-debug-your-container">Remote debug your container&lt;/h2>
&lt;p>Choose one of the following ways to run your docker container: with &lt;code>docker run&lt;/code> or &lt;code>docker-compose&lt;/code>.&lt;/p>
&lt;h3 id="with-docker-run">With docker run&lt;/h3>
&lt;p>Execute the &lt;code>docker run&lt;/code> command using the Docker Image we&amp;rsquo;ve just created.&lt;/p>
&lt;pre>&lt;code>docker run --name demo-spring-boot-docker-debug -e &amp;quot;JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005&amp;quot; -p 8080:8080 -p 5005:5005 ckina/demo-spring-boot-docker-debug
&lt;/code>&lt;/pre>
&lt;h3 id="with-docker-compose">With docker-compose&lt;/h3>
&lt;p>Create a &lt;code>docker-compose.yml&lt;/code> file and execute &lt;code>docker-compose&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-yaml">version: '3.3'
services:
spring-boot:
container_name: demo-spring-boot-docker-debug
build: .
ports:
- '8080:8080'
- '5005:5005'
environment:
- JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
&lt;/code>&lt;/pre>
&lt;p>Options:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--name&lt;/code>: We are naming our container as &lt;code>demo-spring-boot-docker-debug&lt;/code>, which can be changed as needed.&lt;/li>
&lt;li>&lt;code>-p 8080:8080&lt;/code>: Expose our Spring Boot application outside the docker container&lt;/li>
&lt;li>&lt;code>-p 5005:5005&lt;/code>: Expose the port that will be used to connect our IDE with the application to do the remote debug&lt;/li>
&lt;li>&lt;code>-e&lt;/code>: Set environment variables, in this case, we want to use the &lt;code>-agentlib:jdwp&lt;/code> option for debugging. (We will see more details of it later)&lt;/li>
&lt;/ul>
&lt;p>Run:&lt;/p>
&lt;pre>&lt;code>docker-compose up --build
&lt;/code>&lt;/pre>
&lt;p>Verify your endpoint is working by opening this in a browser: &lt;a href="http://localhost:8080/echo?value=foo">http://localhost:8080/echo?value=foo&lt;/a>&lt;/p>
&lt;p>It should give you &lt;code>echo: foo&lt;/code> in the response of the call.&lt;/p>
&lt;h2 id="create-the-remote-debug-configuration-in-intellij-and-attach-the-debugger">Create the remote debug configuration in IntelliJ and attach the debugger&lt;/h2>
&lt;p>Go to &lt;code>Edit Configurations&lt;/code> and create a &lt;code>Remote&lt;/code> config:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/remote-debug-spring-boot-docker-intellij/images/0.png" alt="">&lt;/p>
&lt;p>Note: It&amp;rsquo;s important to have the command line arguments matching with our configuration we introduced into our docker container. In this case this is what should match:&lt;/p>
&lt;pre>&lt;code>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
&lt;/code>&lt;/pre>
&lt;p>Go back to your IDE and do the following:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/remote-debug-spring-boot-docker-intellij/images/1.png" alt="">&lt;/p>
&lt;ol>
&lt;li>Set a breakpoint in your controller, right in the line where the string value is returned&lt;/li>
&lt;li>Click on the debug button&lt;/li>
&lt;li>You should have your IDE connected as the debugger of your Spring Boot application&lt;/li>
&lt;/ol>
&lt;p>Now execute one more time &lt;a href="http://localhost:8080/echo?value=foo">http://localhost:8080/echo?value=foo&lt;/a>&lt;/p>
&lt;p>The breakpoint in the IDE should be activated. We are done.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/remote-debug-spring-boot-docker-intellij/images/2.png" alt="">&lt;/p>
&lt;h2 id="details-on-the-command-line-argument-for-the-remote-debug">Details on the command line argument for the Remote Debug&lt;/h2>
&lt;p>Here we have the details about the &lt;code>-agentlib:jdwp&lt;/code> option we introduced in the environment variables of our docker container:&lt;/p>
&lt;ul>
&lt;li>Format: &lt;code>-agentlib:jdwp=&amp;lt;name1&amp;gt;[=&amp;lt;value1&amp;gt;],&amp;lt;name2&amp;gt;[=&amp;lt;value2&amp;gt;]...&lt;/code>&lt;/li>
&lt;li>&lt;code>transport=dt_socket&lt;/code>: The way you want to connect the debug. It can be done through Socket Transport or Shared Memory Transport. In this example we&amp;rsquo;re using the Socket Transport.&lt;/li>
&lt;li>&lt;code>server=y&lt;/code>: We want to have our application listening to the debugger to be attached, in this case the debugger will be the IDE.&lt;/li>
&lt;li>&lt;code>suspend=n&lt;/code>: We don&amp;rsquo;t want to wait until the debugger is attacher to completely start the Spring Boot application.&lt;/li>
&lt;li>&lt;code>address=*:5005&lt;/code>: Listen for a socket connection on port 5005, which is the port number we want to configure in our IDE.&lt;/li>
&lt;/ul>
&lt;p>More details about the &lt;code>-agentlib:jdwp&lt;/code> option: &lt;a href="https://docs.oracle.com/en/java/javase/14/docs/specs/jpda/conninv.html">https://docs.oracle.com/en/java/javase/14/docs/specs/jpda/conninv.html&lt;/a>&lt;/p>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>Repo: &lt;a href="https://github.com/ckinan/learning/tree/main/java/spring-boot-docker-debug">https://github.com/ckinan/learning/tree/main/java/spring-boot-docker-debug&lt;/a>&lt;/li>
&lt;li>Spring Boot example (some pieces of this example is based on what Spring Boot has in its docs): &lt;a href="https://spring.io/guides/gs/spring-boot-docker/">https://spring.io/guides/gs/spring-boot-docker/&lt;/a>&lt;/li>
&lt;li>Docs for the &lt;code>-agentlib:jdwp&lt;/code> option: &lt;a href="https://docs.oracle.com/en/java/javase/14/docs/specs/jpda/conninv.html">https://docs.oracle.com/en/java/javase/14/docs/specs/jpda/conninv.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cheat Sheet - Git Commands</title><link>https://cesarkina.com/blog/cheat-sheet-git-commands/</link><pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/cheat-sheet-git-commands/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>My Cheat Sheet for: Git Commands.&lt;/p>
&lt;p>Gathering quick references I used to search over and over again. Good to have them handy and supposed to (slowly) grow.&lt;/p>
&lt;h2 id="git-log">git log&lt;/h2>
&lt;p>Git log with some styling (colors and specific order I like to see)&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git log --pretty=format:&amp;quot;%C(Green)%h %C(Red)[.%D.] %C(Blue)[%an] %C(Yellow)[%s] %C(Magenta)[%ad (%ar)]&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="https://cesarkina.com/blog/cheat-sheet-git-commands/images/0.png" alt="">&lt;/p>
&lt;p>Git log, raw info, tabular style&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git log --pretty=format:&amp;quot;%h | %ad | %&amp;gt;&amp;lt;(23,trunc)%ar | %&amp;gt;&amp;lt;(15,trunc)%an | %&amp;lt;(72,trunc)%s | %D&amp;quot; --date=format:&amp;quot;%a %Y-%m-%d %H:%M:%S&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="https://cesarkina.com/blog/cheat-sheet-git-commands/images/1.png" alt="">&lt;/p>
&lt;h2 id="git-diff">git diff&lt;/h2>
&lt;p>Get number of files changed, lines inserted and lines removed between two branches or commits&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git diff --shortstat master..develop
13 files changed, 90 insertions(+), 410 deletions(-)
$ git diff --shortstat 499a153..8059ee2
3 files changed, 69 insertions(+), 1 deletion(-)
&lt;/code>&lt;/pre>
&lt;p>Get diff between two branches or commits with only &lt;strong>relevant&lt;/strong> information (not the whole diff)&lt;/p>
&lt;pre>&lt;code class="language-bash"># File names only
$ git diff --name-only origin/main..develop
blog/intro/index.mdx
generator/MarkdownRenderer.py
resources/404.html
resources/about.html
resources/index.html
resources/layout.html
resources/notes.html
resources/post.html
resources/styles.css
# File names with status (modified, deleted, added)
$ git diff --name-status origin/main..develop
M blog/intro/index.mdx
M generator/MarkdownRenderer.py
M resources/404.html
M resources/about.html
M resources/index.html
M resources/layout.html
M resources/notes.html
M resources/post.html
M resources/styles.css
# With stats
$ git diff --stat origin/main..develop
blog/intro/index.mdx | 19 +++++++++----------
generator/MarkdownRenderer.py | 9 +++------
resources/404.html | 2 +-
resources/about.html | 2 +-
resources/index.html | 2 +-
resources/layout.html | 12 +++++-------
resources/notes.html | 2 +-
resources/post.html | 3 +--
resources/styles.css | 36 ++++++++++--------------------------
9 files changed, 32 insertions(+), 55 deletions(-)
&lt;/code>&lt;/pre>
&lt;h2 id="git-stash">git stash&lt;/h2>
&lt;p>Stash your changes quickly&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git stash
&lt;/code>&lt;/pre>
&lt;p>Stash your changes with a custom message:&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git stash push -m &amp;quot;your message here&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>See your stash entries&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git stash list
stash@{0}: On main: your message here
&lt;/code>&lt;/pre>
&lt;p>See the diff of a given stash entry&lt;/p>
&lt;pre>&lt;code class="language-diff">$ git stash show -p stash@{0}
diff --git a/blog/cheat-sheet-git-commands/index.mdx b/blog/cheat-sheet-git-commands/index.mdx
index 0a038ac..6134c05 100644
--- a/blog/cheat-sheet-git-commands/index.mdx
+++ b/blog/cheat-sheet-git-commands/index.mdx
@@ -6,12 +6,16 @@ slug: cheat-sheet-git-commands
:
(more)
&lt;/code>&lt;/pre>
&lt;p>Save stash diff on disk&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git stash show -p stash@{0} &amp;gt; ~/your_diff.patch
&lt;/code>&lt;/pre>
&lt;p>Apply stash to your current branch. Note: The stash will be removed from the stash store&lt;/p>
&lt;pre>&lt;code class="language-bash">$ git stash pop stash@{0}
&lt;/code>&lt;/pre>
&lt;p>Clean your stash entries&lt;/p>
&lt;pre>&lt;code class="language-bash"># Only one entry
$ git stash drop stash@{2}
# Delete all your stash entries
$ git stash clear
&lt;/code>&lt;/pre>
&lt;h2 id="git-reset">git reset&lt;/h2>
&lt;p>Unstage changes made on certain files. (Changes can be staged again with &lt;code>git add&lt;/code> command)&lt;/p>
&lt;pre>&lt;code class="language-bash">git reset file1.py file2.py
&lt;/code>&lt;/pre>
&lt;h2 id="gitweb">GitWeb&lt;/h2>
&lt;p>To launch git web view&lt;/p>
&lt;pre>&lt;code class="language-bash">git instaweb --httpd=webrick
&lt;/code>&lt;/pre>
&lt;p>And stop with&lt;/p>
&lt;pre>&lt;code class="language-bash">git instaweb --httpd=webrick --stop
&lt;/code>&lt;/pre>
&lt;p>In browser: http://localhost:1234&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/cheat-sheet-git-commands/images/3.png" alt="">&lt;/p>
&lt;p>Ref: &lt;a href="https://git-scm.com/book/en/v2/Git-on-the-Server-GitWeb">https://git-scm.com/book/en/v2/Git-on-the-Server-GitWeb&lt;/a>&lt;/p>
&lt;h2 id="misc">Misc&lt;/h2>
&lt;p>Stage all changed files even the ones living in parent directories of a git repo&lt;/p>
&lt;pre>&lt;code class="language-bash">git add -A
# or...
git add --all
&lt;/code>&lt;/pre>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/a/34957424/7054799">https://stackoverflow.com/a/34957424/7054799&lt;/a> :
&lt;ul>
&lt;li>Q: Git asks for username every time I push&lt;/li>
&lt;li>A: Use SSH keys&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://docs.github.com/en/get-started/getting-started-with-git/setting-your-username-in-git">https://docs.github.com/en/get-started/getting-started-with-git/setting-your-username-in-git&lt;/a>
&lt;ul>
&lt;li>Q: How to set my github username&lt;/li>
&lt;li>A: E.g. &lt;code>git config user.name &amp;quot;ckinan&amp;quot;&lt;/code>&lt;/li>
&lt;li>A: Similar but for email: &lt;code>git config user.email &amp;quot;...@...com&amp;quot;&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr style="height: 1px; background-color: black; border: none;">
&lt;p>See other cheatsheets &lt;a href="https://cesarkina.com/notes#cheatsheets">here&lt;/a>&lt;/p></description></item><item><title>Spring Security - Credentials from JSON request</title><link>https://cesarkina.com/blog/spring-security-credentials-from-json-request/</link><pubDate>Mon, 20 Jul 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/spring-security-credentials-from-json-request/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>One more example of how flexible Spring Security is. This time, we will take a look at how we can override the default behavior to read credentials from any requests to start working with JSON format and authenticate users based on that.&lt;/p>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Once you include the Spring Security dependency into your project, it gives you a lot of features out of the box. A pre-defined login form is one of them, for any protected resource without an authenticated user, the application would automatically redirect you to a Login Page (a form). Any form submission is sent to the endpoint &lt;code>/login&lt;/code> (method POST) and the filter chain of Spring Security takes action. It extracts the username and password parameters from the request and use them to perform the authentication process.&lt;/p>
&lt;p>That is the default behavior coming from Spring Security. In the class &lt;code>UsernamePasswordAuthenticationFilter&lt;/code>, it has a method called &lt;code>attemptAuthentication&lt;/code>, which receives two parameters: a request and a response (&lt;code>HttpServletRequest&lt;/code> and &lt;code>HttpServletResponse&lt;/code>). This class is part of the filter chain (by default), and if there is a login form submission, then the request will pass through there.&lt;/p>
&lt;p>But what if we don&amp;rsquo;t want to send the credentials in a form submission, and our use case requires an endpoint that allows clients to authenticate users by sending their credentials within a JSON body. In that case, the default filter won&amp;rsquo;t support that format. The filter will look at the request-parameters and will try to find the &lt;code>username&lt;/code> and the &lt;code>password&lt;/code> params names. In fact, it is very explicit in the docstrings of the &lt;code>UsernamePasswordAuthenticationFilter&lt;/code> class:&lt;/p>
&lt;blockquote>
&lt;p>Login forms must present two parameters to this filter: a username and password.&lt;/p>
&lt;/blockquote>
&lt;p>Here I will explain one way to get credentials from a JSON request and continue with the rest of the security filter chain.&lt;/p>
&lt;h2 id="scope">Scope&lt;/h2>
&lt;ul>
&lt;li>Create a custom filter to read the credentials from the request in a JSON format and include it in the security filter chain in replacement of the &lt;code>UsernamePasswordAuthenticationFilter&lt;/code>&lt;/li>
&lt;li>No UI. Everything will be tested via Postman&lt;/li>
&lt;li>Set users details in memory&lt;/li>
&lt;/ul>
&lt;h2 id="pre-requisites">Pre-requisites&lt;/h2>
&lt;ul>
&lt;li>Java 8 or higher&lt;/li>
&lt;li>IDE of your preference (Intellij Idea, Eclipse, etc)&lt;/li>
&lt;/ul>
&lt;h2 id="create-project">Create project&lt;/h2>
&lt;p>Go to &lt;a href="http://start.spring.io">http://start.spring.io&lt;/a> and generate a new project. These are the key pieces for this code example:&lt;/p>
&lt;ul>
&lt;li>Project: Gradle&lt;/li>
&lt;li>Language: Java&lt;/li>
&lt;li>Dependencies: Spring Web, Spring Security&lt;/li>
&lt;/ul>
&lt;p>You can use this direct link with all the configuration already set: &lt;a href="https://start.spring.io/#!type=gradle-project&amp;amp;language=java&amp;amp;platformVersion=2.3.1.RELEASE&amp;amp;packaging=jar&amp;amp;jvmVersion=14&amp;amp;groupId=com.ckinan&amp;amp;artifactId=spring-security-credentials-from-json-request&amp;amp;name=spring-security-credentials-from-json-request&amp;amp;description=Spring%20Security%20-%20Credentials%20from%20JSON%20request&amp;amp;packageName=com.ckinan&amp;amp;dependencies=web,security">link&lt;/a>&lt;/p>
&lt;h2 id="create-the-components">Create the components&lt;/h2>
&lt;p>In the project we&amp;rsquo;ve just downloaded, let&amp;rsquo;s create the following classes under the main package (&lt;code>com.ckinan&lt;/code> in my case):&lt;/p>
&lt;ul>
&lt;li>&lt;code>Application&lt;/code>: It comes from the zip file we&amp;rsquo;ve just downloaded. It will start the Spring Boot application for us. No changes required here&lt;/li>
&lt;li>&lt;code>Controller&lt;/code>: Two &amp;ldquo;resources&amp;rdquo; will be exposed in this controller: A protected resource and a public resource (names are self-explanatory)&lt;/li>
&lt;li>&lt;code>SecurityConfiguration&lt;/code>: Contains our security configuration and we will provide the in-memory user to the &lt;code>AuthenticationManager&lt;/code> by using its configurer&lt;/li>
&lt;li>&lt;code>CustomFilter&lt;/code>: Replacement of &lt;code>UsernamePasswordAuthenticationFilter&lt;/code> in the filter chain. It will read the credentials from the JSON payload coming within the &lt;code>HttpServletRequest&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="project-structure">Project Structure&lt;/h3>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-credentials-from-json-request/images/0.png" alt="">&lt;/p>
&lt;h2 id="review-the-components">Review the components&lt;/h2>
&lt;h3 id="controller">Controller&lt;/h3>
&lt;pre>&lt;code class="language-java">package com.ckinan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
@RequestMapping(&amp;quot;/protected-resource&amp;quot;)
public String protectedResource() {
return &amp;quot;The Protected Resource&amp;quot;;
}
@RequestMapping(&amp;quot;/public-resource&amp;quot;)
public String publicResource() {
return &amp;quot;The Public Resource&amp;quot;;
}
}
&lt;/code>&lt;/pre>
&lt;p>Includes two endpoints (both &lt;code>GET&lt;/code> method) that basically return some string values:&lt;/p>
&lt;ul>
&lt;li>&lt;code>/protected-resource&lt;/code>: This endpoint will be protected by our Spring Security configuration. By &amp;ldquo;protected&amp;rdquo;, it means clients need to authenticate first to access the resource.&lt;/li>
&lt;li>&lt;code>/public-resource&lt;/code>: Authentication is not needed to access this resource.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Note: Later we will define the security restrictions for both endpoints in &lt;code>SecurityConfiguration&lt;/code> class.&lt;/p>
&lt;/blockquote>
&lt;h3 id="customfilter">CustomFilter&lt;/h3>
&lt;pre>&lt;code class="language-java">package com.ckinan;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
public class CustomFilter extends AbstractAuthenticationProcessingFilter {
protected CustomFilter() {
super(new AntPathRequestMatcher(&amp;quot;/login&amp;quot;, &amp;quot;POST&amp;quot;));
}
@Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username, password;
try {
Map&amp;lt;String, String&amp;gt; requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = requestMap.get(&amp;quot;username&amp;quot;);
password = requestMap.get(&amp;quot;password&amp;quot;);
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
&lt;/code>&lt;/pre>
&lt;p>This filter is the one in charge of reading the credentials from the request in JSON format. It will act as replacement of the &lt;code>UsernamePasswordAuthenticationFilter&lt;/code>, which we are removing from our spring security configuration in favor of the new &lt;code>CustomFilter&lt;/code>.&lt;/p>
&lt;p>The constructor is passing an &lt;code>AntPathRequestMatcher&lt;/code> object with two params:&lt;/p>
&lt;ul>
&lt;li>&lt;code>pattern&lt;/code>: Any request coming from the &lt;code>/login&lt;/code> path will be considered by this filter to perform the authentication process. It could be anything that matches your needs as API pattern or path, for example: &lt;code>/api/login&lt;/code> or &lt;code>/sign-in&lt;/code> or whatever&lt;/li>
&lt;li>&lt;code>httpMethod&lt;/code>: We want to process any request as &lt;code>POST&lt;/code> method&lt;/li>
&lt;/ul>
&lt;p>We are also overriding the method &lt;code>attemptAuthentication&lt;/code>, which is going to extract the &lt;code>username&lt;/code> and &lt;code>password&lt;/code> from the request JSON payload. There are certainly multiple ways to extract the JSON values and assign them into java objects, but for now, I&amp;rsquo;m using &lt;code>ObjectMapper&lt;/code> and assigning the values to a &lt;code>Map&lt;/code>. Later they will be assigned to their corresponding &lt;code>String&lt;/code> variables.&lt;/p>
&lt;p>Once we get the credentials, we need to perform the &lt;code>authenticate&lt;/code> process, which is provided by the &lt;code>AuthenticationManager&lt;/code>. Quoting from the docs &lt;a href="https://spring.io/guides/topicals/spring-security-architecture">https://spring.io/guides/topicals/spring-security-architecture&lt;/a> :&lt;/p>
&lt;blockquote>
&lt;p>Spring Boot provides a default global AuthenticationManager (with just one user) unless you pre-empt it by providing your own bean of type AuthenticationManager. The default is secure enough on its own for you not to have to worry about it much, unless you actively need a custom global AuthenticationManager. If you do any configuration that builds an AuthenticationManager you can often do it locally to the resources that you are protecting and not worry about the global default.&lt;/p>
&lt;/blockquote>
&lt;p>In this example, we are configuring our in-memory user (see later in our &lt;code>SecurityConfiguration&lt;/code> class) into the &lt;code>AuthenticationManager&lt;/code>. So any &lt;code>username&lt;/code> and &lt;code>password&lt;/code> from a JSON payload will be verified against the in-memory user we have there. I think this is the &lt;em>meat&lt;/em> of this example.&lt;/p>
&lt;p>For that to happen, the method &lt;code>authenticate&lt;/code> of the &lt;code>AuthenticationManager&lt;/code> receives a token, which in this case we are initializing a &lt;code>UsernamePasswordAuthenticationToken&lt;/code> with the credetials coming from the request.&lt;/p>
&lt;p>One important note on this, if you visit the &lt;code>UsernamePasswordAuthenticationFilter&lt;/code> class (from the codebase of Spring Security), you will notice that I&amp;rsquo;m trying to mimic the logic in its &lt;code>attemptAuthentication&lt;/code> method, which by default it gets the credentials coming as form parameters (&lt;a href="https://github.com/spring-projects/spring-security/blob/97ccbe5df20c1f9999784d2d0d84d159965bdaca/web/src/main/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java#L76-L103">link&lt;/a>)&lt;/p>
&lt;h3 id="securityconfiguration">SecurityConfiguration&lt;/h3>
&lt;pre>&lt;code class="language-java">package com.ckinan;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(&amp;quot;cesar&amp;quot;).password(&amp;quot;{noop}s3cr3t&amp;quot;).roles(&amp;quot;USER&amp;quot;);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomFilter mupaf = new CustomFilter();
mupaf.setAuthenticationManager(authenticationManager());
http
.csrf().disable()
.addFilterAt(
mupaf,
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.GET, &amp;quot;/protected-resource&amp;quot;).authenticated()
.antMatchers(HttpMethod.POST, &amp;quot;/login&amp;quot;).permitAll();
}
}
&lt;/code>&lt;/pre>
&lt;p>For simplicity, here we are working with only one user loaded from memory:&lt;/p>
&lt;ul>
&lt;li>Username: &lt;code>cesar&lt;/code>&lt;/li>
&lt;li>Password: &lt;code>s3cr3t&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>The &lt;code>{noop}&lt;/code> means we are going to use the &lt;code>NoOpPasswordEncoder&lt;/code> password encoder, which simply does not do anything to the password we are providing &lt;code>s3cr3t&lt;/code> (it won&amp;rsquo;t get encoded). This is not secure obviously, but will work for this example.&lt;/p>
&lt;p>The &lt;code>AuthenticationManagerBuilder&lt;/code> will allow us to configure the above in-memory user into the &lt;code>AuthenticationManager&lt;/code>. So that any request that passes through the &lt;code>CustomFilter&lt;/code>, will try to &amp;ldquo;authenticate&amp;rdquo; the incoming credentials against this user details manager.&lt;/p>
&lt;p>Creates new instance of the &lt;code>CustomFilter&lt;/code>, in which we want to provide the &lt;code>authenticationManager&lt;/code> we should already have configured at this point. Then insert the filter in the same position as the &lt;code>UsernamePasswordAuthenticationFilter&lt;/code> would have been.&lt;/p>
&lt;p>Then, configure the &lt;code>antMatchers&lt;/code> for the two resources (one public one protected).&lt;/p>
&lt;blockquote>
&lt;p>Note: I need another space to explore the &lt;code>csrf&lt;/code> configuration. For now, we need to disable the feature, otherwise requests won&amp;rsquo;t get until the &lt;code>CustomFilter&lt;/code> within the filter chain&lt;/p>
&lt;/blockquote>
&lt;h2 id="run-and-test">Run and test&lt;/h2>
&lt;p>Run the application with the following instruction in terminal (need to cd to the project directory):&lt;/p>
&lt;pre>&lt;code>$ ./gradlew bootRun
&lt;/code>&lt;/pre>
&lt;p>Testing with Postman, it will fail trying to access the &lt;code>protected-resource&lt;/code> because the user is not authenticated (yet)&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-credentials-from-json-request/images/1.png" alt="">&lt;/p>
&lt;p>Use the &lt;code>/login&lt;/code> endpoint to authenticate the user. The &lt;code>Not Found&lt;/code> error from the response refers to the &lt;code>/&lt;/code> resource which we simply don&amp;rsquo;t have in our controller. Remember we only have set two endpoints: &lt;code>protected-resource&lt;/code> and &lt;code>public-resource&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-credentials-from-json-request/images/2.png" alt="">&lt;/p>
&lt;p>Now, try to access the &lt;code>protected-resource&lt;/code> once again. This time, the user should be already authenticated.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-credentials-from-json-request/images/3.png" alt="">&lt;/p>
&lt;p>At any point of time, with or without a successful authentication, the &lt;code>public-resource&lt;/code> should be accessible.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-credentials-from-json-request/images/4.png" alt="">&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;ul>
&lt;li>During this example, I noticed there is another area to explore: how AuthenticationManager, ProviderManager and AuthenticationProvider are connected all together to allow the application setup a custom authentication process.&lt;/li>
&lt;li>Nothing prevents us to customize the way we extract credentials from the requests. And one more time, it proves the flexibility of Spring Security to do some work like that. All I did was to mimic what &lt;code>UsernamePasswordAuthenticationFilter&lt;/code> does to capture the username and password from a &lt;code>HttpServletRequest&lt;/code> object.&lt;/li>
&lt;li>It&amp;rsquo;s pretty important to be aware of the filters that Spring Security provides. There is a comprehensive list of filters in this &lt;a href="https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-security-filters">link&lt;/a>. So if there is another behavior we want to customize in regards of what is happening in the filter chain, then we need to know where exactly (and how) that should happen. A breakpoint in &lt;code>FilterChainProxy&lt;/code> is a good point to start.&lt;/li>
&lt;/ul>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>Github repo: &lt;a href="https://github.com/ckinan/learning/tree/main/java/spring-security-credentials-from-json-request">https://github.com/ckinan/learning/tree/main/java/spring-security-credentials-from-json-request&lt;/a>&lt;/li>
&lt;li>Spring Security Architecture (gave me more insights about what AuthenticationManager, ProviderManager and AuthenticationProvider are): &lt;a href="https://spring.io/guides/topicals/spring-security-architecture">https://spring.io/guides/topicals/spring-security-architecture&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Spring Security - Filter Chain</title><link>https://cesarkina.com/blog/spring-security-filter-chain/</link><pubDate>Fri, 03 Jul 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/spring-security-filter-chain/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>After playing with few basic configurations of Spring Security, I noticed there is something important to understand before moving to check other features in the framework, which is its &lt;strong>Filter Chain&lt;/strong>. This time I&amp;rsquo;ll take a look at how Spring Security acts on the requests coming from the client, which involves a set of Filters, each of them with a very specific goal.&lt;/p>
&lt;blockquote>
&lt;p>Note: This is not a tutorial, this is just some notes of what I understand (as of today) about Spring Security and its Filter Chain.&lt;/p>
&lt;/blockquote>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>The most complete explanation I could find regarding how the Security Filter Chain is built in the entire architecture is in the official documentation in here: &lt;a href="https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture">https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture&lt;/a>&lt;/p>
&lt;p>I&amp;rsquo;ll write down what I interpret from what I already interacted with the module in combination with what the documentation is trying to explain.&lt;/p>
&lt;h2 id="main-components">Main Components&lt;/h2>
&lt;p>There are five pieces (in the documentation I pointed out in the previous section) that I want to visit here. They are:&lt;/p>
&lt;h3 id="filters">Filters&lt;/h3>
&lt;p>First of all, &amp;ldquo;Filters&amp;rdquo; (in the Servlet&amp;rsquo;s world) came from the &lt;code>Java Specification Requests (JSR) #53&lt;/code>. Look at this:&lt;/p>
&lt;ul>
&lt;li>Ref: &lt;a href="https://jcp.org/en/jsr/detail?id=53">https://jcp.org/en/jsr/detail?id=53&lt;/a>&lt;/li>
&lt;li>Direct link to the PDF (See Page #43): &lt;a href="https://download.oracle.com/otn-pub/jcp/7840-servlet-2.3-spec-oth-JSpec/servlet-2_3-fcs-spec.pdf?AuthParam=1593651764_bd0a8306800ffcc92df386c00a6c89f0">https://download.oracle.com/otn-pub/jcp/7840-servlet-2.3-spec-oth-JSpec/servlet-2_3-fcs-spec.pdf?AuthParam=1593651764_bd0a8306800ffcc92df386c00a6c89f0&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Filters are some pieces of code that do something before or after a Request gets processed by a Servlet. The web container (e.g. Tomcat) will know that.&lt;/p>
&lt;p>Filters have multiple purposes, it is not only restricted to be for Security mechanisms. They are meant to read or write (any logic in general) with the Request. For example: one filter can print some audit logs, another one can add some headers to the request, and so on.&lt;/p>
&lt;p>In the Spring Security&amp;rsquo;s world, we have a set of Filters strategically ordered to prevent subsequent filters or the controller itself being called if they find the request not matching with the security configuration.&lt;/p>
&lt;p>The most straightforward example may be, imagine a user entering a wrong password, there is one filter dedicated to verify whether the password is correct or not. If not, then the at some point of the Filter Chain, one of the Filters will reject the request and respond with a 401 status code to the client.&lt;/p>
&lt;h3 id="delegatingfilterproxy">DelegatingFilterProxy&lt;/h3>
&lt;p>&lt;code>DelegatingFilterProxy&lt;/code> is a class registered as Standard Filter in the Container, but the container doesn&amp;rsquo;t know anything about Spring Beans at this point. &lt;code>DelegatingFilterProxy&lt;/code> is the support to establish communication between the Standard Filters and the Spring Application Context.&lt;/p>
&lt;p>Notice that I didn&amp;rsquo;t mention anything about Spring Security yet, that&amp;rsquo;s because &lt;code>DelegatingFilterProxy&lt;/code> is a class located in Spring Web and not Spring Security. Which is interesting because from there I interpret that this proxy can be used by something else not necessarily related to Spring Security.&lt;/p>
&lt;h3 id="filterchainproxy">FilterChainProxy&lt;/h3>
&lt;p>&lt;code>FilterChainProxy&lt;/code> is a filter located in Spring Security module.&lt;/p>
&lt;p>It takes a list of filters and creates something called &lt;code>VirtualFilterChain&lt;/code> (a private class within &lt;code>FilterChainProxy&lt;/code>), which is going to take the list of the Security Filters and &lt;em>start&lt;/em> the chain.&lt;/p>
&lt;p>I want to point this out that seems to be pretty useful, quoting Spring Security docs:&lt;/p>
&lt;blockquote>
&lt;p>First, it provides a starting point for all of Spring Security’s Servlet support. For that reason, if you are attempting to troubleshoot Spring Security’s Servlet support, adding a debug point in FilterChainProxy is a great place to start.&lt;/p>
&lt;/blockquote>
&lt;h3 id="securityfilterchain">SecurityFilterChain&lt;/h3>
&lt;p>&lt;code>SecurityFilterChain&lt;/code> contains the list of all the filters involved in Spring Security. This interface expose a method &lt;code>List&amp;lt;Filter&amp;gt; getFilters()&lt;/code> that returns all the filters such as the &lt;code>UsernamePasswordAuthenticationFilter&lt;/code> or &lt;code>LogoutFilter&lt;/code>.&lt;/p>
&lt;p>In Spring Security, one or more &lt;code>SecurityFilterChain&lt;/code>s can be registered in the &lt;code>FilterChainProxy&lt;/code>.&lt;/p>
&lt;p>The following picture illustrates all the components I tried to explain so far:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-filter-chain/images/0.png" alt="">&lt;/p>
&lt;p className="text-sm text-center">SecurityFilterChain, by &lt;a href="https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-securityfilterchain" target="_blank" rel="noopener noreferrer">Spring Security Docs&lt;/a>&lt;/p>
&lt;h3 id="security-filters">Security Filters&lt;/h3>
&lt;p>Finally, at the end of all this hierarchy (Standard Filter &amp;gt; DelegatingFilterProxy &amp;gt; FilterChainProxy &amp;gt; SecurityFilterChain), there is list of possible security filters that we can configure in our applications. I&amp;rsquo;ll just mention the ones that I kind of interated so far:&lt;/p>
&lt;ul>
&lt;li>LogoutFilter&lt;/li>
&lt;li>UsernamePasswordAuthenticationFilter&lt;/li>
&lt;li>DefaultLoginPageGeneratingFilter&lt;/li>
&lt;li>BasicAuthenticationFilter&lt;/li>
&lt;li>SessionManagementFilter&lt;/li>
&lt;/ul>
&lt;p>In fact, everytime your Spring Boot application starts up, the list of filters are printed in the logs by default (in their corresponding order of execution). Example:&lt;/p>
&lt;pre>&lt;code>// Raw logs at startup time
// List of filters are printed there
2020-07-02 21:34:55.896 INFO 51095 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7fe82967, org.springframework.security.web.context.SecurityContextPersistenceFilter@9596ce8, org.springframework.security.web.header.HeaderWriterFilter@26f46fa6, org.springframework.security.web.csrf.CsrfFilter@558756be, org.springframework.security.web.authentication.logout.LogoutFilter@80bfdc6, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6edcad64, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@75ae4a1f, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@476fe690, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@50850539, org.springframework.security.web.session.SessionManagementFilter@227a47, org.springframework.security.web.access.ExceptionTranslationFilter@3968bc60, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@2924f1d8]
// And this is the list of filters in a more readable way:
[
1. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@5fa23c
2. org.springframework.security.web.context.SecurityContextPersistenceFilter@73c9e8e8
3. org.springframework.security.web.header.HeaderWriterFilter@59f93db8
4. org.springframework.security.web.csrf.CsrfFilter@13d9261f
5. org.springframework.security.web.authentication.logout.LogoutFilter@b606cb6
6. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@2a3194c6
7. org.springframework.security.web.savedrequest.RequestCacheAwareFilter@de8039f
8. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4b790d86
9. org.springframework.security.web.authentication.AnonymousAuthenticationFilter@558756be
10. org.springframework.security.web.session.SessionManagementFilter@1aabf50d
11. org.springframework.security.web.access.ExceptionTranslationFilter@2e8a1ab4
12. org.springframework.security.web.access.intercept.FilterSecurityInterceptor@51c959a4
]
&lt;/code>&lt;/pre>
&lt;p>So, any requests coming from the client will pass through the filter chain and in that order.&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;ul>
&lt;li>I started to write this article thinking of the Filter Chain as something strictily related to Spring Security. BUT, during all my reading I got that this part of the architecture in regards of how Spring Security Filters are added into the actual &amp;ldquo;standard&amp;rdquo; filter chain as something that you first need to know other &amp;ldquo;external&amp;rdquo; concepts about Filters, Servlets and the interfaces that Spring Web provides. Only then, all the illustrations and explanation in the official docs started to make sense to me.&lt;/li>
&lt;li>Debugging one of my few &lt;a href="https://github.com/ckinan/learning/tree/main/java/spring-security-02">simple examples&lt;/a> during startup time helped me a lot to understand some pieces, specifically having some breakpoints in &lt;code>FilterChainProxy&lt;/code> then going all the way down and all the way up in the whole call hierarchy was pretty useful. Still, there are A LOT of areas in the codebase of Spring Security and Spring Web that I don&amp;rsquo;t understand right now..&lt;/li>
&lt;li>I&amp;rsquo;m impressed the quality of the docstrings Spring has in its &lt;a href="https://github.com/spring-projects/spring-security">codebase&lt;/a>, I found a lot of insights there, like examples, code snippets, explanation in details (more than I could find in the official docs).&lt;/li>
&lt;/ul>
&lt;h2 id="refs">Refs&lt;/h2>
&lt;ul>
&lt;li>This is where most part of this post comes from: &lt;a href="https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture">https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture&lt;/a>&lt;/li>
&lt;li>Architecture Deep Dive in Spring Security - Joe Grandja @ Spring I/O 2017: &lt;a href="https://www.youtube.com/watch?v=8rnOsF3RVQc">https://www.youtube.com/watch?v=8rnOsF3RVQc&lt;/a>&lt;/li>
&lt;li>SpringOne 2GX 2012 - Getting Started with Spring Security 3.1 - Rob Winch: &lt;a href="https://www.youtube.com/watch?v=k32KqrckLEE">https://www.youtube.com/watch?v=k32KqrckLEE&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Spring Security - Form Login &amp; Logout</title><link>https://cesarkina.com/blog/spring-security-form-login-logout/</link><pubDate>Sun, 28 Jun 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/spring-security-form-login-logout/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>I am still in the process of learning more about Spring Security. This time I created a simple example that involves Form Login and Logout customizations.&lt;/p>
&lt;h2 id="scope">Scope&lt;/h2>
&lt;ul>
&lt;li>Happy path: Public page -&amp;gt; Login Page -&amp;gt; Protected resource&lt;/li>
&lt;li>Non-happy path: Public page -&amp;gt; Login Page -&amp;gt; Login Error Page&lt;/li>
&lt;li>Ability to logout once the user is authenticated (it&amp;rsquo;s on the Protected resource)&lt;/li>
&lt;li>Customize the urls for login and logout that come by default from Spring Security&lt;/li>
&lt;li>Create public and protected resources. This time, let&amp;rsquo;s do some html (server side rendered)&lt;/li>
&lt;li>Create custom view to show whenever a login attempt fails&lt;/li>
&lt;li>Focus only in these two configurers: Form Login &amp;amp; Logout&lt;/li>
&lt;/ul>
&lt;h2 id="out-of-scope">Out of scope&lt;/h2>
&lt;ul>
&lt;li>Do not create custom users. Let&amp;rsquo;s keep using the default user and password generated by Spring Security to avoid other distractions&lt;/li>
&lt;li>Do not concentrate in file/project structure, let&amp;rsquo;s keep it simple in one single file&lt;/li>
&lt;/ul>
&lt;h2 id="pre-requisites">Pre-requisites&lt;/h2>
&lt;ul>
&lt;li>Java 8 or higher&lt;/li>
&lt;li>IDE of your preference (Intellij Idea, Eclipse, etc)&lt;/li>
&lt;/ul>
&lt;h2 id="create-project">Create project&lt;/h2>
&lt;p>Go to &lt;a href="https://start.spring.io/">https://start.spring.io/&lt;/a> and add the following dependencies:&lt;/p>
&lt;ul>
&lt;li>Spring Web&lt;/li>
&lt;li>Spring Security&lt;/li>
&lt;li>Thymeleaf (we will create some html templates to render the views in server-side)&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/0.png" alt="">&lt;/p>
&lt;p>Generate, download and open the project in your IDE. I&amp;rsquo;ll use Intellij IDEA.&lt;/p>
&lt;h2 id="create-the-components">Create the components&lt;/h2>
&lt;p>In the project that we just downloaded, let&amp;rsquo;s edit the &lt;code>SpringSecurity02Application.java&lt;/code> file, which is our &lt;code>SpringBootApplication&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>Create a class &lt;code>SecurityConfiguration&lt;/code>, which extends &lt;code>WebSecurityConfigurerAdapter&lt;/code>. We will override the &lt;code>configure(HttpSecurity)&lt;/code> method to setup our login and logout processes&lt;/li>
&lt;li>Create a class &lt;code>Controller&lt;/code>, which will provide the endpoints for our views (login, logout, protected and public resources)&lt;/li>
&lt;/ul>
&lt;p>Then, under the &lt;code>resources&lt;/code> folder of our application, in this case &lt;code>spring-security-02/src/main/resources/templates&lt;/code>, let&amp;rsquo;s create the four templates we want to render in our flow:&lt;/p>
&lt;ul>
&lt;li>login.html&lt;/li>
&lt;li>login-error.html&lt;/li>
&lt;li>protected-resource.html&lt;/li>
&lt;li>public-resource.html&lt;/li>
&lt;/ul>
&lt;p>The project structure should look like:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/2.png" alt="">&lt;/p>
&lt;p>The above is all what we will create (in terms of files and classes). Now let&amp;rsquo;s move to the details.&lt;/p>
&lt;p>This is how our code should look like for the whole example (repo available on &lt;a href="https://github.com/ckinan/learning/tree/main/java/spring-security-02">Github&lt;/a>):&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/1.png" alt="">&lt;/p>
&lt;h2 id="review-the-components">Review the components&lt;/h2>
&lt;p>From the previous picture, let&amp;rsquo;s take a look at each component and what each piece means.&lt;/p>
&lt;p>&lt;strong>Block #1. The Form Login Setup&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>formLogin()&lt;/code>: Let Spring Security know we want to support authentication through a login form&lt;/li>
&lt;li>&lt;code>loginPage(&amp;quot;/custom-login&amp;quot;)&lt;/code>: Set the custom login url to GET the login form rendered and also to submit (POST) the form&lt;/li>
&lt;li>&lt;code>usernameParameter(&amp;quot;myUser&amp;quot;)&lt;/code>: Specify the parameter name to get the username from the request&lt;/li>
&lt;li>&lt;code>passwordParameter(&amp;quot;myPass&amp;quot;)&lt;/code>: Same than previous but for the password&lt;/li>
&lt;li>&lt;code>defaultSuccessUrl(&amp;quot;/protected-resource&amp;quot;)&lt;/code>: URL that we want to be &lt;em>redirected&lt;/em> once it gets a successful login attempt&lt;/li>
&lt;li>&lt;code>failureHandler(...)&lt;/code>: Provide a handler for any failed requests. This time, we want to configure a &lt;code>SimpleUrlAuthenticationFailureHandler&lt;/code>, which is going to &lt;em>redirect&lt;/em> the user to the given url in the constructor, in this case it&amp;rsquo;s &lt;code>/login-error&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Block #2. The Logout Setup&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>logout()&lt;/code>: Allow our application to customize the login &amp;ldquo;default&amp;rdquo; setup&lt;/li>
&lt;li>&lt;code>logoutUrl(&amp;quot;/custom-logout&amp;quot;)&lt;/code>: URL that will handle the logout process&lt;/li>
&lt;li>&lt;code>logoutSuccessUrl(&amp;quot;/&amp;quot;)&lt;/code>: Similar to &lt;code>defaultSuccessUrl&lt;/code>, but for the logout process. The url that we want to be &lt;em>redirected&lt;/em> once it gets a successful logout attempt&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Block #3. The controller&lt;/strong>&lt;/p>
&lt;p>The controller has four request handlers, all of them provide server-rendered views that will serve to our example to have the login authentication flow. Most of these views will work with the template engine &lt;a href="https://www.thymeleaf.org/doc/articles/springmvcaccessdata.html">Thymeleaf&lt;/a> to access data from the application&amp;rsquo;s context.&lt;/p>
&lt;ul>
&lt;li>&lt;code>/custom-login&lt;/code>: Serve the &lt;code>login.html&lt;/code> view:&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-html">spring-security-02/src/main/resources/templates/login.html
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xmlns:th=&amp;quot;https://www.thymeleaf.org&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Login Page&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Login Page&amp;lt;/h1&amp;gt;
&amp;lt;form th:action=&amp;quot;@{/custom-login}&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
&amp;lt;div&amp;gt;
Username: &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;myUser&amp;quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
Password: &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;myPass&amp;quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Log in&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>In this template:&lt;/p>
&lt;ol>
&lt;li>Action &lt;code>/custom-login&lt;/code> will send the content (POST) from the client (the browser) to the server (our application)&lt;/li>
&lt;li>Username and password &lt;code>&amp;lt;input&amp;gt;&lt;/code>&amp;rsquo;s names should match with the &lt;code>usernameParameter(...)&lt;/code> and &lt;code>passwordParameter(...)&lt;/code> we specified to the &lt;code>formLogin()&lt;/code> setup&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;code>/login-error&lt;/code>: Serve the &lt;code>login-error.html&lt;/code> view:&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-html">spring-security-02/src/main/resources/templates/login-error.html
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xmlns:th=&amp;quot;https://www.thymeleaf.org&amp;quot;&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Login Error Page&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Login Error Page&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;Reason: &amp;lt;span th:text=&amp;quot;${session.SPRING_SECURITY_LAST_EXCEPTION.message}&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Go to &amp;lt;a href=&amp;quot;/custom-login&amp;quot;&amp;gt;Login Page&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>In this template:&lt;/p>
&lt;ol>
&lt;li>Print the error message for any failed login attempt. The error message is coming from an exception thrown by Spring Security and added into the session attribute: &lt;code>SPRING_SECURITY_LAST_EXCEPTION&lt;/code>&lt;/li>
&lt;li>A link that redirects to the login page&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>Note: Usually, we don&amp;rsquo;t need a separate page just to inform that provided credentials are wrong. But I wanted to create this one for the sake of exploring this feature&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;code>/protected-resource&lt;/code>: Serve the &lt;code>protected-resource.html&lt;/code> view:&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-html">spring-security-02/src/main/resources/templates/protected-resource.html
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xmlns:th=&amp;quot;https://www.thymeleaf.org&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Protected Resource&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Protected Resource&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;Hey, I am a protected resource. Only authenticated users can see me.&amp;lt;/p&amp;gt;
&amp;lt;form th:action=&amp;quot;@{/custom-logout}&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Logout&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>In this template:&lt;/p>
&lt;ol>
&lt;li>Points to the action &lt;code>/custom-logout&lt;/code>. It should match with what we set in &lt;code>logoutUrl(...)&lt;/code> during the &lt;code>logout()&lt;/code> setup.&lt;/li>
&lt;li>Notice the method for this form is &lt;code>POST&lt;/code>. According to the documentation:&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>If CSRF protection is enabled (default), then the request must also be a POST. This means that by default POST &amp;ldquo;/logout&amp;rdquo; is required to trigger a log out. If CSRF protection is disabled, then any HTTP method is allowed.&lt;/p>
&lt;/blockquote>
&lt;p>In our example, we didn&amp;rsquo;t disabled the CSRF protection, so we want to do &lt;code>POST&lt;/code>. More about CSRF: &lt;a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">https://en.wikipedia.org/wiki/Cross-site_request_forgery&lt;/a>&lt;/p>
&lt;ul>
&lt;li>&lt;code>/&lt;/code>: Serve the &lt;code>public-resource.html&lt;/code> view:&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-html">&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Public Resource&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Public Resource&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;Hey, I am a public resource. Anyone can see me.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Login to see protected resources. &amp;lt;a href=&amp;quot;/custom-login&amp;quot;&amp;gt;Login Page&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code>&lt;/pre>
&lt;ol>
&lt;li>This view can be accessed without being authenticated. Remember that we only require authentication for the &lt;code>protected-resource&lt;/code>, which we defined in our &lt;code>SecurityConfiguration&lt;/code> class:&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-java"> .authorizeRequests()
.antMatchers(HttpMethod.GET, &amp;quot;/protected-resource&amp;quot;).authenticated();
&lt;/code>&lt;/pre>
&lt;h2 id="run-and-test">Run and test&lt;/h2>
&lt;ol>
&lt;li>Run the application with the following instruction in terminal (need to &lt;code>cd&lt;/code> to the project directory):&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-bash">$ ./gradlew bootRun
&lt;/code>&lt;/pre>
&lt;ol start="2">
&lt;li>Grab the auto-generated password from logs and wait until Spring Boot is fully initialized:&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>Using generated security password: 0c6fc7f8-34b1-4c04-a0f6-dbaa8eb8c21f
&lt;/code>&lt;/pre>
&lt;ol start="3">
&lt;li>Open &lt;a href="http://localhost:8080">http://localhost:8080&lt;/a> in a browser&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/3.png" alt="">&lt;/p>
&lt;p>This is the public resource (no need to authenticate so far)&lt;/p>
&lt;ol start="4">
&lt;li>Click on the &amp;ldquo;Login Page&amp;rdquo; link and the Login Form should be rendered. Enter &lt;code>user&lt;/code> as the username (defaulted by Spring Security) and the auto-generated password. Then click on &amp;ldquo;Log in&amp;rdquo; button&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/4.png" alt="">&lt;/p>
&lt;ol start="5">
&lt;li>The protected resource should be displayed&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/5.png" alt="">&lt;/p>
&lt;ol start="6">
&lt;li>If during login form credentials are invalid, then &lt;code>login-error&lt;/code> view should be displayed&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-form-login-logout/images/6.png" alt="">&lt;/p>
&lt;ol start="7">
&lt;li>If user is not authenticated and tries to access &lt;code>protected-resource&lt;/code> through the browser via &lt;a href="http://localhost:8080/protected-resource">http://localhost:8080/protected-resource&lt;/a> , then the login page should be displayed. We didn&amp;rsquo;t do anything but just telling Spring Security that resource needs authentication, then Spring Security is responsible of redirect the request to the Login Page&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>From this example, I learned the following:&lt;/p>
&lt;ul>
&lt;li>The more I read about Spring Security, the more flexibility I find in there, which is awesome. The problem is that I (personally) learn by doing small things in the most isolated way. That is why I approached this post to only elaborate on Login and Logout very specific configurations. There are more features in those configurers that I didn&amp;rsquo;t take, but at least this example helped me to know what to see, what to expect and where to look for help, which is something for the next point.&lt;/li>
&lt;li>Some pieces of code in this example where just copied from the Spring Security docstrings. For example:&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.html#loginPage-java.lang.String-">https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.html#loginPage-java.lang.String-&lt;/a>&lt;/p>
&lt;p>In the above link, I found a &lt;code>Example login.jsp&lt;/code>, which gave me a clear idea of what I have to have in &lt;code>login.html&lt;/code>. I think Spring Security team makes a great job documenting their code.&lt;/p>
&lt;ul>
&lt;li>Found really useful navigate Spring Security codebase and check the classes hierarchies, method usages, stuff like that. I downloaded the &lt;a href="https://github.com/spring-projects/spring-security">spring-security repo&lt;/a> and do that exercise any time I needed to look further&lt;/li>
&lt;li>There are still some things I need to understand in the whole architecture of Spring Security. To me, how the request is redirected using the handlers is sort of mistery. I&amp;rsquo;d like to see how the filter chain works under the hood, and several other concepts I usually see when I google for something. I guess I&amp;rsquo;ll figure them out when the time comes.&lt;/li>
&lt;/ul>
&lt;h2 id="refs">Refs&lt;/h2>
&lt;ul>
&lt;li>Github Repo: &lt;a href="https://github.com/ckinan/learning/tree/main/java/spring-security-02">https://github.com/ckinan/learning/tree/main/java/spring-security-02&lt;/a>&lt;/li>
&lt;li>Spring Security - Docs: &lt;a href="https://docs.spring.io/spring-security/site/docs/current/api/overview-summary.html">https://docs.spring.io/spring-security/site/docs/current/api/overview-summary.html&lt;/a>&lt;/li>
&lt;li>Thymeleaf - Access data: &lt;a href="https://www.thymeleaf.org/doc/articles/springmvcaccessdata.html">https://www.thymeleaf.org/doc/articles/springmvcaccessdata.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>KnockoutJS - Register Component</title><link>https://cesarkina.com/blog/knockoutjs-register-component/</link><pubDate>Wed, 17 Jun 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/knockoutjs-register-component/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>One more time, I convinced myself how important is sometimes get away from my codebase and isolate my tests to understand foundations. This time, got something with KnockoutJS and how components are registered.&lt;/p>
&lt;h2 id="motivation">Motivation&lt;/h2>
&lt;p>Recently I wanted to understand how a KnockoutJS components can be created. So, I went into the official docs and found good explanations about it:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://knockoutjs.com/documentation/component-overview.html">https://knockoutjs.com/documentation/component-overview.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Instead of trying things directly to the codebase I wanted to introduce new components, I first wanted to create really simple examples in an isolated way.&lt;/p>
&lt;p>I created a codepen for this excercise, which helped me a lot to quickly make changes and play with the things I was learning during this.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://codepen.io/ckinan/pen/eYJBjYR">https://codepen.io/ckinan/pen/eYJBjYR&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="playground">Playground&lt;/h2>
&lt;p>The following component called &lt;code>my-component&lt;/code> configures a &lt;code>viewModel&lt;/code> that creates a new object that handles the states of the values in the form. Also, tells the components to be synchronously loaded and finally declares the template to be rendered in the DOM.&lt;/p>
&lt;pre>&lt;code class="language-js">var MyViewModel = function(first, last) {
this.firstName = ko.observable('Cesar');
this.lastName = ko.observable('K');
this.fullName = ko.pureComputed(function() {
return this.firstName() + &amp;quot; &amp;quot; + this.lastName();
}, this);
};
ko.components.register('my-component', {
viewModel: {
createViewModel: function(params) {
return new MyViewModel(params);
},
},
synchronous: true,
template: `
&amp;lt;div&amp;gt;
&amp;lt;p&amp;gt;First name: &amp;lt;input data-bind=&amp;quot;textInput: firstName&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Last name: &amp;lt;input data-bind=&amp;quot;textInput: lastName&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;strong&amp;gt;Hello, &amp;lt;span data-bind=&amp;quot;text: fullName&amp;quot;&amp;gt; &amp;lt;/span&amp;gt;!&amp;lt;/strong&amp;gt;
&amp;lt;/div&amp;gt;
`
});
&lt;/code>&lt;/pre>
&lt;p>I got 4 ways to use components (probably there are more variations). For now, this is what I got.&lt;/p>
&lt;h2 id="approach-1">Approach #1&lt;/h2>
&lt;p>Bind the component by specifying its name directly.&lt;/p>
&lt;pre>&lt;code class="language-html">&amp;lt;div data-bind='component: &amp;quot;my-component&amp;quot;'&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code>&lt;/pre>
&lt;h2 id="approach-2">Approach #2&lt;/h2>
&lt;p>Bind the component by specifying an object with the name of it.&lt;/p>
&lt;pre>&lt;code class="language-html">&amp;lt;div data-bind='component: { name: &amp;quot;my-component&amp;quot; }'&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>Note: In that object, you can also pass the params to the component if there is any. This example only passes the name.&lt;/p>
&lt;blockquote>
&lt;p>So far, approaches #1 and #2 are taken from here &lt;a href="https://knockoutjs.com/documentation/component-binding.html">https://knockoutjs.com/documentation/component-binding.html&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 id="approach-3">Approach #3&lt;/h2>
&lt;p>Using component as &amp;ldquo;Custom elements&amp;rdquo;. Ref: &lt;a href="https://knockoutjs.com/documentation/component-custom-elements.html">https://knockoutjs.com/documentation/component-custom-elements.html&lt;/a>&lt;/p>
&lt;pre>&lt;code class="language-html">&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;
&lt;/code>&lt;/pre>
&lt;h2 id="approach-4">Approach #4&lt;/h2>
&lt;p>Just like in Approach #2, but in Javascript having one component to inject into another component.&lt;/p>
&lt;p>New component for this example is a divider. (Only a template, no view models this time)&lt;/p>
&lt;pre>&lt;code class="language-js">ko.components.register('my-divider-component', {
template: `
&amp;lt;div style=&amp;quot;border-top: 3px solid #bbb;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
`
});
&lt;/code>&lt;/pre>
&lt;p>Then, create an object that will provide the information about the component (the name and optionally the params).&lt;/p>
&lt;pre>&lt;code class="language-js">const MyDividerComponent = function() {
const self = this;
self.component = () =&amp;gt; {
return {
name: 'my-divider-component'
}
};
};
const myDividerComponent = new MyDividerComponent();
&lt;/code>&lt;/pre>
&lt;p>Finally, inject this component into another component while creating the template. (I&amp;rsquo;ll use &lt;code>my-component&lt;/code> example but will add one more line in the template)&lt;/p>
&lt;pre>&lt;code class="language-js">ko.components.register('my-component', {
viewModel: {
createViewModel: function(params) {
return new MyViewModel(params);
},
},
synchronous: true,
template: `
&amp;lt;div&amp;gt;
&amp;lt;p&amp;gt;First name: &amp;lt;input data-bind=&amp;quot;textInput: firstName&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Last name: &amp;lt;input data-bind=&amp;quot;textInput: lastName&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;strong&amp;gt;Hello, &amp;lt;span data-bind=&amp;quot;text: fullName&amp;quot;&amp;gt; &amp;lt;/span&amp;gt;!&amp;lt;/strong&amp;gt;
&amp;lt;div data-bind=&amp;quot;component: myDividerComponent.component()&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
`
});
&lt;/code>&lt;/pre>
&lt;p>That way, &lt;code>myDividerComponent.component()&lt;/code> will actually return this object:&lt;/p>
&lt;pre>&lt;code class="language-json">{
name: 'my-divider-component'
}
&lt;/code>&lt;/pre>
&lt;p>With that object, the template will inject the component just like I explained in approach #2.&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>With this simple examples, I just want to point out two main things:&lt;/p>
&lt;ol>
&lt;li>KnockoutJS has multiple ways to use a component. I&amp;rsquo;m just aware of the four ones in this article, which is good for now to understand some things that kind of overwhelmed me before doing these excercises.&lt;/li>
&lt;li>I am finding it super useful when I create this &amp;ldquo;playground&amp;rdquo; things to understand something without all the distractions that a big codebase can have (not complaining, just my way to learn&amp;hellip; maybe). Easy to share, easy to remember, easy to extend or copy.&lt;/li>
&lt;/ol></description></item><item><title>Spring Security - Getting Started</title><link>https://cesarkina.com/blog/spring-security-getting-started/</link><pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/spring-security-getting-started/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>Getting started with Spring Security by doing a simple example to understand the basics.&lt;/p>
&lt;h2 id="pre-requisites">Pre-requisites&lt;/h2>
&lt;ul>
&lt;li>Java 8 or higher&lt;/li>
&lt;li>IDE of your preference (Intellij Idea, Eclipse, etc)&lt;/li>
&lt;/ul>
&lt;h2 id="create-project">Create project&lt;/h2>
&lt;p>Go to &lt;a href="https://start.spring.io/">https://start.spring.io/&lt;/a> and add the following dependencies:&lt;/p>
&lt;ul>
&lt;li>Spring Web&lt;/li>
&lt;li>Spring Security&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/0.png" alt="">&lt;/p>
&lt;p>Click on the &lt;code>GENERATE&lt;/code> button. A zip file should be downloaded. Unzip it and we are ready to start.&lt;/p>
&lt;h2 id="run-the-application">Run the application&lt;/h2>
&lt;p>&lt;code>cd&lt;/code> to the project directory, and execute the following:&lt;/p>
&lt;pre>&lt;code class="language-bash">$ ./mvnw spring-boot:run
&lt;/code>&lt;/pre>
&lt;p>Logs should look like:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/1.png" alt="">&lt;/p>
&lt;ol>
&lt;li>Spring Security provides you a default password (for a default user) out of the box just by adding the Spring Security dependency. In the above screenshot, the password is &lt;code>c916dd57-3b15-49e8-906e-163c7aadf1b5&lt;/code> for the user &lt;code>user&lt;/code>.&lt;/li>
&lt;li>The filter chain created for your requests. There are 15 filters in the chain added for you and in the order they are printed in the logs within the array. If you open a text editor and put all them in one line each, you should see something like:&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@f951a7f
org.springframework.security.web.context.SecurityContextPersistenceFilter@360bc645
org.springframework.security.web.header.HeaderWriterFilter@6cd5122d
org.springframework.security.web.csrf.CsrfFilter@3fe46690
org.springframework.security.web.authentication.logout.LogoutFilter@79fd6f95
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6726cc69
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@2241f05b
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@20cece0b
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5300cac
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5d51e129
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@109a2025
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4c777e7b
org.springframework.security.web.session.SessionManagementFilter@10c07b8d
org.springframework.security.web.access.ExceptionTranslationFilter@b16e202
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@fd9ebde
&lt;/code>&lt;/pre>
&lt;p>Each of these have a purpose, and they make the whole flow of the security layer possible. Remember, they are filters chained that will take actions in that order whenever a new request comes.&lt;/p>
&lt;ol start="3">
&lt;li>The port that your application is running. In this case, it&amp;rsquo;s in 8080.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Create a rest controller&lt;/li>
&lt;li>Secure a REST API&lt;/li>
&lt;/ul>
&lt;h2 id="open-the-application">Open the application&lt;/h2>
&lt;p>Open &lt;a href="http://localhost:8080">http://localhost:8080&lt;/a> in your browser, a login page should be displayed:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/2.png" alt="">&lt;/p>
&lt;p>By default and by just adding the Spring Security dependency to the project, it generates a login page that sends the credentials to a generated endpoint &lt;code>/login&lt;/code> that is in charge of verify whether the username and password are valid for the application and give access to the protected resources.&lt;/p>
&lt;p>Let&amp;rsquo;s enter the credentials printed in logs into the form and click on the &lt;code>Sign in&lt;/code> button (or just press enter).&lt;/p>
&lt;ul>
&lt;li>Username: &lt;code>user&lt;/code>&lt;/li>
&lt;li>Password: &lt;code>c916dd57-3b15-49e8-906e-163c7aadf1b5&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>An error page is displayed:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/3.png" alt="">&lt;/p>
&lt;p>This is expected. In order to understand what happened, let&amp;rsquo;s go to the Network Tab in the Dev Tools.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/4.png" alt="">&lt;/p>
&lt;ol>
&lt;li>The credentials were sent to the endpoint &lt;code>login&lt;/code>. Spring Security created that for us for free and the request will be processed by the filter chain we saw in the logs earlier.&lt;/li>
&lt;li>Information are sent from the pre-generated form. Notice the keys are already pre-defined &lt;code>username&lt;/code>, &lt;code>password&lt;/code> and a third one &lt;code>_csrf&lt;/code>, which is meant to tackle the CSRF vulnerabilities. All them will be interpreted by the &lt;code>login&lt;/code> endpoint.&lt;/li>
&lt;/ol>
&lt;p>So, why we get an error page if I utilized the correct credentials? Let&amp;rsquo;s see the second record in the Network Tab.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/5.png" alt="">&lt;/p>
&lt;ol>
&lt;li>The resource that was asked was: &lt;code>http://localhost:8080/&lt;/code>. Think of it as it requested the &lt;code>/&lt;/code> resource from &lt;code>http://localhost:8080&lt;/code>.&lt;/li>
&lt;li>404 (not found). It means, the resource &lt;code>/&lt;/code> doesn&amp;rsquo;t exist.&lt;/li>
&lt;li>The referer or who had initiated that request was &lt;code>http://localhost:8080/login&lt;/code> which is the location where the credentials were sent through the form.&lt;/li>
&lt;/ol>
&lt;p>The error message refers to a resource that was not found. And that is because Spring Security only provides us the security stuff for the resources we want to protect. But, as we didn&amp;rsquo;t touch anything in the codebase, actually we didn&amp;rsquo;t even open the project in the IDE, then there is nothing to protect (yet) and the resource &lt;code>/&lt;/code> (I suspect) is the default path right after a successful login is attempted.&lt;/p>
&lt;p>One more thing to notice, in Application Tab there in the Dev Tools, there is a new Cookie created under the name &lt;code>JSESSION&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/6.png" alt="">&lt;/p>
&lt;p>That is what keeps the already-logged-in user in session in the Spring Security context.&lt;/p>
&lt;p>Finally, there are still more particulars working behind the scenes, in the following link there is a list what is happening to Spring Boot once the Spring Security dependency is included into the project.&lt;/p>
&lt;p>Link: &lt;a href="https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-hello-auto-configuration">https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-hello-auto-configuration&lt;/a>&lt;/p>
&lt;p>A lot of things have happened by just adding the Spring Security dependency into the project. I didn&amp;rsquo;t write any single line of code to get all the above stuff with Spring Security out of the box.&lt;/p>
&lt;p>Now, let&amp;rsquo;s go to the codebase and some documentation to understand where all these came from.&lt;/p>
&lt;h2 id="the-content-of-the-project">The content of the project&lt;/h2>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/7.png" alt="">&lt;/p>
&lt;p>The above is all what the project has. Actually, in &lt;a href="https://start.spring.io/">https://start.spring.io/&lt;/a> , there is a button &lt;code>EXPLORE&lt;/code> that you can see the content of the zip before downloading it. Well, as I didn&amp;rsquo;t touch a thing, that project tree should be exactly the same as I downloaded (except for the auto-generated stuff of my IDE).&lt;/p>
&lt;p>There are 3 key files that I&amp;rsquo;d like to mention so far:&lt;/p>
&lt;ol>
&lt;li>&lt;code>SpringSecurity01Application&lt;/code> is the class that initiates the application. In fact, this is not a Spring Security thing but from Spring Boot.&lt;/li>
&lt;li>&lt;code>mvnw&lt;/code>, this is the script that we utilized to run our application with the command &lt;code>./mvnw spring-boot:run&lt;/code>. This is only one way to start the application, you can also run this by executing the generated &lt;code>jar&lt;/code> after packaging the component with maven, maybe other techniques. For now, I&amp;rsquo;ll keep continue using this method.&lt;/li>
&lt;li>&lt;code>pom.xml&lt;/code> registered two important dependencies:&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-xml">&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;spring-boot-starter-security&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>The most relevant one is &lt;code>spring-boot-starter-security&lt;/code>, which is giving us all the security features that we saw so far in this example. The other one &lt;code>spring-boot-starter-web&lt;/code> is will enable the RESTful endpoints that are utilized in this example.&lt;/p>
&lt;p>From here, there are a lot of features to incorporate into the project, and there are several ways to do the same thing. For now I&amp;rsquo;ll do a simple exercise that will allow me to explain couple of more things.&lt;/p>
&lt;h2 id="securing-a-rest-api">Securing a REST API&lt;/h2>
&lt;p>I&amp;rsquo;ll write a very simple REST API that will just return a string value and have Spring Security protecting the endpoint.&lt;/p>
&lt;h3 id="create-the-controller-class">Create the Controller Class&lt;/h3>
&lt;p>Let&amp;rsquo;s create two endpoints. One protected and one public.&lt;/p>
&lt;pre>&lt;code class="language-java">// ./Controller.java
package com.ckinan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
@RequestMapping(&amp;quot;/protected-resource&amp;quot;)
public String protectedResource() {
return &amp;quot;The Protected Resource&amp;quot;;
}
@RequestMapping(&amp;quot;/public-resource&amp;quot;)
public String publicResource() {
return &amp;quot;The Public Resource&amp;quot;;
}
}
&lt;/code>&lt;/pre>
&lt;p>In this controller, there is no evidence of security for any of these endpoints. The controller doesn&amp;rsquo;t even know they will be protected. They could, but we will have that in a separate class. For now, it&amp;rsquo;s important to know the following:&lt;/p>
&lt;ul>
&lt;li>The endpoint &lt;code>/protected-resource&lt;/code> will be protected by Spring Security with some basic instructions we will set later in a separate class.&lt;/li>
&lt;li>The endpoint &lt;code>/public-resource&lt;/code> will be publicly available. Meaning, no authentication will be required.&lt;/li>
&lt;/ul>
&lt;h3 id="create-the-security-configuration-class">Create the Security Configuration Class&lt;/h3>
&lt;p>All the configuration will live in a separate class in this step.&lt;/p>
&lt;pre>&lt;code class="language-java">// ./SecurityConfiguration.java
package com.ckinan;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, &amp;quot;/protected-resource&amp;quot;).authenticated()
.antMatchers(HttpMethod.GET, &amp;quot;/public-resource&amp;quot;).permitAll();
}
}
&lt;/code>&lt;/pre>
&lt;p>In this configuration class we define the security features we want to add to our endpoints. Let&amp;rsquo;s see some key points:&lt;/p>
&lt;ul>
&lt;li>&lt;code>@EnableWebSecurity&lt;/code>: This annotation will set effectively the Spring Security configuration we define in the class. It works together with the class we are extending: &lt;code>WebSecurityConfigurerAdapter&lt;/code>.&lt;/li>
&lt;li>&lt;code>WebSecurityConfigurerAdapter&lt;/code>: When we extends this class, we are definiting a new instance of the &lt;code>WebSecurityConfigurer&lt;/code> interface. We are overriding one of its methods &lt;code>configure(HttpSecurity http)&lt;/code> to have our custom version of how we want to protect our endpoints.&lt;/li>
&lt;li>&lt;code>configure(HttpSecurity http)&lt;/code>: This is the method we want to override to tell Spring Security that we only want to protect one of our two endpoints and not both, which would be the default behavior if we don&amp;rsquo;t do this.&lt;/li>
&lt;li>&lt;code>HttpSecurity http&lt;/code>: The input parameter of the method is the one containing all the configuration that Spring Security will consider during the execution of all the Filter Chain to any requests coming to the application.&lt;/li>
&lt;li>&lt;code>formLogin()&lt;/code>: Enables the Login (default) screen for any request to our protected endpoint&lt;/li>
&lt;li>&lt;code>authorizeRequests()&lt;/code>: The restrictions to the requests&lt;/li>
&lt;li>&lt;code>antMatchers&lt;/code>: Indicates the method and the endpoints (patterns) for each restrictions we want to give&lt;/li>
&lt;li>&lt;code>authenticated() and permitAll()&lt;/code>: I think they are self-explainatory. But basically, we specify which antMatcher we want to retrict to only be accessed if it&amp;rsquo;s authenticated user or not&lt;/li>
&lt;/ul>
&lt;h3 id="run-the-application-1">Run the application&lt;/h3>
&lt;p>Let&amp;rsquo;s run the &lt;code>mvnw&lt;/code> script once again and test the two endpoints in the browser to see how they behave with the given configuration.&lt;/p>
&lt;pre>&lt;code class="language-bash">$ ./mvnw spring-boot:run
# Note: The autogenerated password changes every time you run this command. This time I got:
Using generated security password: 5e57b493-6181-4682-bc21-81ec40222298
&lt;/code>&lt;/pre>
&lt;p>Try to open the public endpoint: &lt;a href="http://localhost:8080/public-resource">http://localhost:8080/public-resource&lt;/a>&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/8.png" alt="">&lt;/p>
&lt;p>The public resource doesn&amp;rsquo;t require any authentication process, which is expected since we are telling Spring Security to allow ALL requests to access it with this matcher: &lt;code>.antMatchers(HttpMethod.GET, &amp;quot;/public-resource&amp;quot;).permitAll()&lt;/code>.&lt;/p>
&lt;p>Now let&amp;rsquo;s try with the protected endpoint: &lt;a href="http://localhost:8080/protected-resource">http://localhost:8080/protected-resource&lt;/a>&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/9.png" alt="">&lt;/p>
&lt;p>This time, right after we hit the URL in the browser, a login page is displayed. The login page was intentionally configured by giving the &lt;code>formLogin()&lt;/code> to the &lt;code>http&lt;/code> variable within the &lt;code>configure(...)&lt;/code> method in our &lt;code>SecurityConfiguration&lt;/code> class.&lt;/p>
&lt;p>Now lets use the username by default and the password printed in the logs when we started the application.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/10.png" alt="">&lt;/p>
&lt;p>By entering the correct credentials, the application will allow the user to access the protected resource. If we give incorrect credential, then the login page will let us know, like so:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/spring-security-getting-started/images/11.png" alt="">&lt;/p>
&lt;h2 id="final-thoughs">Final thoughs&lt;/h2>
&lt;p>There are a lot of security concepts that I still need to study. Somewhat during this process of learning Spring Security I am exposed to understand some of those security definitions to apply the configurations accordingly.&lt;/p>
&lt;p>I first tried to read all the official docs at once from Spring Security, but it was quite overwhelming, and I then preferred to create a project, and do things that later forced me to investigate more specific topics by the time I was facing some issues with the setup.&lt;/p>
&lt;p>For now, I feel I learned few concepts that will allow me to expand later, perhaps in future articles.&lt;/p>
&lt;h2 id="refs">Refs:&lt;/h2>
&lt;ul>
&lt;li>Github Repo: &lt;a href="https://github.com/ckinan/learning/tree/main/java/spring-security-01">https://github.com/ckinan/learning/tree/main/java/spring-security-01&lt;/a>&lt;/li>
&lt;li>Spring Security Reference Docs: &lt;a href="https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/">https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/&lt;/a>&lt;/li>
&lt;li>Spring Security - Its 15 minutes tutorial: &lt;a href="https://spring.io/guides/gs/securing-web/">https://spring.io/guides/gs/securing-web/&lt;/a>&lt;/li>
&lt;li>I strongly recommend to read this article, which helped me a lot to understand some foundations of Spring Security: &lt;a href="https://www.marcobehler.com/guides/spring-security">https://www.marcobehler.com/guides/spring-security&lt;/a>&lt;/li>
&lt;li>Also, I found the Spring One videos on Youtube very informative: &lt;a href="https://www.youtube.com/watch?v=8rnOsF3RVQc&amp;amp;t">https://www.youtube.com/watch?v=8rnOsF3RVQc&amp;amp;t&lt;/a> and &lt;a href="https://www.youtube.com/watch?v=WbnuwpSBXPs">https://www.youtube.com/watch?v=WbnuwpSBXPs&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Tailwind CSS - First Steps</title><link>https://cesarkina.com/blog/tailwind-css-first-steps/</link><pubDate>Sun, 17 May 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/tailwind-css-first-steps/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>In my whole career, even when I was in college, every time I had to build an application that involves UI or styles, I always struggled with the design. After some years now, I still have the same problem. Colors, text size, layouts, etc are simply a nightmare to me. This is one of the things I usually try to avoid in web development when there is some CSS file I have to (inevitable) make some changes, I just say &amp;ldquo;oh God, please help me&amp;rdquo;.&lt;/p>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Ultimately, I&amp;rsquo;ve been hearing a lot about Tailwind CSS, a utility-first framework that comes with a bunch of amazing stuff for your design process, like for example predefined attributes to create a nice green button with rounded corners and a hover effect, but Tailwind doesn&amp;rsquo;t give you the whole class for the button, instead it brings with the necessary little pieces that you have to combine nicely to build the design for that button.&lt;/p>
&lt;p>This is an example of how a green button using Tailwind CSS would look like:&lt;/p>
&lt;pre>&lt;code class="language-html">&amp;lt;button
type=&amp;quot;button&amp;quot;
class=&amp;quot;rounded bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 border-solid border-2 border-green-600 m-5&amp;quot;&amp;gt;
Login
&amp;lt;/button&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="https://cesarkina.com/blog/tailwind-css-first-steps/images/0.png" alt="">&lt;/p>
&lt;p>Someone might say, &amp;ldquo;why would I need that if there are other tools or frameworks that provide the whole button component styles out of the box&amp;rdquo;. Well, that was me. But, after giving it a try, I noticed that you can get exactly what the other frameworks can give you, but now you know exactly what you are including in your styles. Meaning, you get full control of how things are presented in your UI.&lt;/p>
&lt;h2 id="motivation">Motivation&lt;/h2>
&lt;p>There are a couple of things I really like about Tailwind CSS:&lt;/p>
&lt;ul>
&lt;li>Documentation: Nice examples, well explained and I go there every time I need to do an isolated thing before doing it in my project.&lt;/li>
&lt;li>Easy to get started: When I want to try something new, one of the first things I look for is the &amp;ldquo;Get Started&amp;rdquo; section. Well, here it is &lt;a href="https://tailwindcss.com/docs/installation/">Get Started&lt;/a>. I went to the &amp;ldquo;Using CDN&amp;rdquo; section because I just wanted to get a quick demo to explore.&lt;/li>
&lt;li>Community: I see more and more people using it and sharing their projects or knowledge about it. One thing that convinced me to take a look at it was one guy replicating a whole application using Tailwind CSS. I was like &amp;ldquo;what? is that possible?&amp;rdquo;, so cool.&lt;/li>
&lt;/ul>
&lt;p>Another thing I got from there, and this might be something more &amp;ldquo;personal&amp;rdquo;, is that after I started using it I got myself more interested in to know more properties I can use in CSS, because, Tailwind can give you the utilities you need, but in order to use them, you have to know how you would accomplish that with plain CSS, then go to Tailwind docs and look for it without creating a single CSS line, you have everything you need available to use in your HTML.&lt;/p>
&lt;p>It&amp;rsquo;s like my development process, in terms of design, changed quite a bit. I now feel I have more control of how I build my UI. Now, my steps to accomplish some styling are (example):&lt;/p>
&lt;ol>
&lt;li>
&lt;p>What do I need? I need a green button for my login page. Specs: Green background, white text, border and rounded corners.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>How is this in plain CSS? Example for the green background: &lt;code>background-color: #48bb78;&lt;/code> . I think this is the most difficult step at least in my case, there are a lot of things in CSS that are unknown to me.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Go to Tailwind CSS docs and search for the CSS property. Link: &lt;a href="https://tailwindcss.com/docs/background-color">https://tailwindcss.com/docs/background-color&lt;/a> . For the Green Background, the Tailwind CSS &amp;ldquo;equivalent class&amp;rdquo; should be &lt;code>class=&amp;quot;bg-green-500&amp;quot;&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Isolate the code and test. You can also test directly in your project, but I strongly recommend to test any style in a separate environment, so if something fails, you are not confused by the question &amp;ldquo;is it because of something in my project or is it with Tailwind or is it me doing something wrong?&amp;rdquo; (you know what I mean&amp;hellip;) Example:&lt;/p>
&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-html">&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;link
rel=&amp;quot;stylesheet&amp;quot;
type=&amp;quot;text/css&amp;quot;
href=&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.4.6/tailwind.min.css&amp;quot;
/&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;button
type=&amp;quot;button&amp;quot;
class=&amp;quot;rounded bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 border-solid border-2 border-green-600 m-5&amp;quot;
&amp;gt;
Login
&amp;lt;/button&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code>&lt;/pre>
&lt;ol start="5">
&lt;li>Incorporate this in your project. You should make the changes according to your project needs once you get the desired results in your isolated tests.&lt;/li>
&lt;/ol>
&lt;p>Example in codepen: &lt;a href="https://codepen.io/ckinan/pen/JjYePyW">https://codepen.io/ckinan/pen/JjYePyW&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>Tailwind CSS is a different way to see design in my development process. It didn&amp;rsquo;t magically solve all my problems with the design, and I don&amp;rsquo;t see it as something that I needed to do my styles looking better, but it kind of helped me to do more things directly in the HTML files, instead of doing it in CSS files just to make my design unique. Finally, the feeling of having control of every single detail in how my components look like without having to write any much CSS, keeps me motivated to continue utilizing Tailwind.&lt;/p>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>Tailwind CSS: &lt;a href="https://tailwindcss.com/">https://tailwindcss.com/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Project Github Pull Request Viewer</title><link>https://cesarkina.com/blog/gh-pr-viewer/</link><pubDate>Fri, 24 Apr 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/gh-pr-viewer/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>How I built a pull request viewer using Github API. What I learned and what I failed.&lt;/p>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>I am maintaining a software that its repo is in Github. That means, I had to get familiar with the tools this platform provides. Code Reviews with Pull Request, which is one of the features Github has, where you can interact with who owns the code changes before merging a branch into master.&lt;/p>
&lt;p>As a reviewer, I have to open pull requests regularly to do my review. Meaning, more than likely I have to visit &lt;a href="https://github.com/pulls">https://github.com/pulls&lt;/a> frequently to see whichh PRs I have in my queue.&lt;/p>
&lt;p>After some time seeing my list of PRs, I noticed there are cases where I would like to see more information in that list before clicking on one of those. Like for example, the number of approvals each PR already have, or if there is already activity such comments, requested changes, and who did which action, so with all these information in that same list I can have a clearer idea what&amp;rsquo;s going on there. Yeah, I know it&amp;rsquo;s only one click away from seeing all of that, but as I was about to study the Github API, I decided to give it a try.&lt;/p>
&lt;p>Before going to the details, I want to start showing two images that somewhat explains what is this all about.&lt;/p>
&lt;p>Official Github Pull Requests View looks like this:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/gh-pr-viewer/images/0.png" alt="">&lt;/p>
&lt;p>My clone looks like this:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/gh-pr-viewer/images/1.png" alt="">&lt;/p>
&lt;h2 id="expectations">Expectations&lt;/h2>
&lt;h3 id="the-need">The need&lt;/h3>
&lt;p>A different way to see the list of pull requests that could give me a better visibility of which code reviews I have in my queue and need my attention.&lt;/p>
&lt;h3 id="the-approach">The approach&lt;/h3>
&lt;p>Make use of Github API to read my pull requests based on my username and show valuable information within a web application.&lt;/p>
&lt;h3 id="the-minimum-value">The (minimum) value&lt;/h3>
&lt;p>At least, I wanted to have:&lt;/p>
&lt;ol>
&lt;li>Last updated date-time&lt;/li>
&lt;li>Counts for current reviewer who: approved, requested changes, commented, awaiting within each pull request&lt;/li>
&lt;li>Events in a timeline&lt;/li>
&lt;/ol>
&lt;p>There are some more &amp;ldquo;features&amp;rdquo; I included in this project for different reasons, like Authentication (for obvious reasons), a search form, and some basic filtering functionality.&lt;/p>
&lt;h2 id="the-jorney">The Jorney&lt;/h2>
&lt;h3 id="the-stack">The Stack&lt;/h3>
&lt;p>It was the time to choose the stack and tools to develop this web application. Here is the summary of what I got and why:&lt;/p>
&lt;table border="1">
&lt;thead>
&lt;tr>
&lt;th>The Stack&lt;/th>
&lt;th>The Why&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://reactjs.org/">ReactJS&lt;/a>&lt;/td>
&lt;td>I am not a frontend guy, but I had some little projects (mostly TODO apps) that I had built previously and this time I wanted to create something &amp;quot;more elaborated&amp;quot; and get a better understanding of the basics.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://developer.github.com/v3">Github API&lt;/a>&lt;/td>
&lt;td>A must if I wanted to read data from Github&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.netlify.com/products/functions/">Netlify Functions&lt;/a> (Javascript)&lt;/td>
&lt;td>I needed a backend service to hide the Secret Key to make calls to Github API. I was not sure which service and programming language to choose. My first option was to create a Java or NodeJS service and deploy it on Heroku, but I didn&amp;#39;t want to pay for this to be &amp;quot;live&amp;quot; forever (no sleep time). I wanted this to be totally free. That is why I decided to go with Netlify Functions, which doesn&amp;#39;t have that kind of restrictions and it&amp;#39;s free, at least for this small project that won&amp;#39;t have too much calls, I said &amp;quot;it&amp;#39;s good&amp;quot;.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://primer.style/css/">Primer CSS&lt;/a>&lt;/td>
&lt;td>I didn&amp;#39;t want to spend time styling this web application. I literally just googled &amp;quot;github css styles&amp;quot; and found this cool thing that saved me a lot of time.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="the-process">The process&lt;/h3>
&lt;h4 id="1-github-api-preliminary-research">1. Github API (Preliminary Research)&lt;/h4>
&lt;p>First of all, I had to do some research about how to consume Github API, there are some previous articles I posted about it:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://cesarkina.com/blog/github-api-first-steps/">Github API First glance&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cesarkina.com/blog/github-api-oauth-app/">Github API OAuth tokens for apps&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>By doing that, I was able to confirm if what I wanted to do was possible. Meaning: Reading Pull Requests from the API was viable.&lt;/p>
&lt;h4 id="2-create-the-frontend">2. Create the frontend&lt;/h4>
&lt;p>I was surprised how easy is to create a brand new web application and get it running. The &lt;code>create-react-app&lt;/code> CLI was my guy and made me forget all my setup problems I had some time ago while following some tutorials that are well explained but I guess I am not that smart.&lt;/p>
&lt;h4 id="3-create-the-backend">3. Create the backend&lt;/h4>
&lt;p>I had some reads about how use Netlify Functions to accomplish specific goals:&lt;/p>
&lt;ul>
&lt;li>Goal #1: How to use Environment Variables. This is actually the most important one, because I wanted to have the Github Secret Key hidden in one of these Variables.&lt;/li>
&lt;li>Goal #2: How to use its &amp;ldquo;develop mode&amp;rdquo;. Ref: &lt;a href="https://www.netlify.com/products/dev/">https://www.netlify.com/products/dev/&lt;/a>&lt;/li>
&lt;li>Goal #3: How to handle cookies (my limited knowledge about secury led me to simply pass tokens using a cookie).&lt;/li>
&lt;/ul>
&lt;h4 id="4-connect-the-front-with-the-back">4. Connect the front with the back&lt;/h4>
&lt;p>It was the most interesting part. From here I learned few things:&lt;/p>
&lt;p>&lt;strong>A) GraphQL&lt;/strong>&lt;/p>
&lt;p>This is the best thing I&amp;rsquo;ve discovered within this project. The first version of this project was to use the Github API v3, whis expose the endpoints with REST.&lt;/p>
&lt;p>The only one thing that made me switch to v4 (GraphQL) was the number of requests I had to make to get Pull Requests information. Why is that? Because every time I wanted the list of pull requests and their details I had to make a Request to the endpoint to fetch the users, then the repos, then the pull requests, then the reviews, and so on. Nested loops everywhere, without mentioning that the JSON responses had data that I didn&amp;rsquo;t even need.&lt;/p>
&lt;p>With GraphQL, I only had to be worried about one single endpoint and try my queries I previously tested within the &lt;a href="https://developer.github.com/v4/guides/using-the-explorer/">GraphQL Explorer&lt;/a>.&lt;/p>
&lt;p>After switching to GraphQL I finally got the idea of using it, and I am pretty sure there are pros and cons about it, but at least for this project, it was a must.&lt;/p>
&lt;p>&lt;strong>B) I do know nothing about security&lt;/strong>&lt;/p>
&lt;p>While developing the interface of my Netlify Functions, I was thinking how to prevent users from executing my APIs without any permissions. There were a lot of techniques and approaches I was not able to understand. I had to apply maybe one of the easiest ways to avoid being attacked by something from the client side. My method was basically the use of cookies to store the token after being authenticated.&lt;/p>
&lt;p>I also developed two ways to be &amp;ldquo;authenticated&amp;rdquo; and consume the API:&lt;/p>
&lt;ul>
&lt;li>Method #1: Authorizing OAuth Apps. Ref: &lt;a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/">https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/&lt;/a>&lt;/li>
&lt;li>Method #2: Store a Personal Access Token as Environment Variable. (Only meant to be used locally)&lt;/li>
&lt;/ul>
&lt;p>&lt;del>I believe this is the main reason I can&amp;rsquo;t get this project deployed in the cloud, because I can&amp;rsquo;t figure this thing out yet&amp;hellip;&lt;/del> . UPDATE: Deployed on Netlify: &lt;a href="https://gh-pr-viewer.netlify.app/">https://gh-pr-viewer.netlify.app/&lt;/a>&lt;/p>
&lt;h4 id="5-test">5. Test&lt;/h4>
&lt;p>After having the minimum things already developed, I forced myself to move from the official Github PRs view to my start using my application. It was a good move, I mean, things were going well, I had the information I originally said I need, few bugs but working.&lt;/p>
&lt;p>During this stage, I had to refactor several parts of the code. Here I learned a little more about React Hooks, React Router, TypeScript and got used to this ecosystem more than I initially expected. I know I am not in the position to say I am now a &amp;ldquo;React&amp;rdquo; guy, but good enough to connect pieces and understand what&amp;rsquo;s going on with the components and data state flows. However, I can&amp;rsquo;t say &amp;ldquo;I know React&amp;rdquo;, because I am pretty sure the code of this app has a lot of &amp;ldquo;spaghetti&amp;rdquo; and it&amp;rsquo;s not using React in the right way.&lt;/p>
&lt;h2 id="reality">Reality&lt;/h2>
&lt;p>So far so good. I have a web application, running in my local machine, reading pull requests information directly from the API. I actually use this daily to see what I have in my queue to do code review.&lt;/p>
&lt;p>However, I don&amp;rsquo;t think I accomplished my original need: &amp;ldquo;Better visibility of my code reviews&amp;rdquo;. I have a web application that just show me things in a &amp;ldquo;different way&amp;rdquo;, but doesn&amp;rsquo;t make any difference in comparison with the official view from Github.&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>In terms of &amp;ldquo;learnings&amp;rdquo;, I gained a lot from this project, because I was able to get some ReactJS skills, discovered one of the advantages of using GraphQL, and made some things with Serverless Functions (Netlify). But in terms of &amp;ldquo;solving my needs&amp;rdquo;, I think I failed, because it is irrelevant which view use (the Github one or mine), this app doesn&amp;rsquo;t make any difference at least in the current state.&lt;/p>
&lt;p>Using something you are creating in your daily basis speeds up the development, because you realize what are the things you need to do immmediately or features you want to have or the bugs you need to fix, it&amp;rsquo;s like you are your own client, which keeps you motivated.&lt;/p>
&lt;p>Moving forward, I&amp;rsquo;ll be still making more updates to this project, but for now, I don&amp;rsquo;t think this is going to be as regular as it was the past weeks.&lt;/p>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>Repo: &lt;a href="https://github.com/ckinan/gh-pr-viewer">https://github.com/ckinan/gh-pr-viewer&lt;/a>&lt;/li>
&lt;li>Netlify instance: &lt;a href="https://gh-pr-viewer.netlify.app/">https://gh-pr-viewer.netlify.app/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Github API - OAuth tokens for apps</title><link>https://cesarkina.com/blog/github-api-oauth-app/</link><pubDate>Fri, 13 Mar 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/github-api-oauth-app/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>How to use OAuth tokens for apps with Github API to authorize the access of certain data on behalf of the logged in user.&lt;/p>
&lt;h2 id="github-web-flow">Github Web flow&lt;/h2>
&lt;p>Quoting Github docs:&lt;/p>
&lt;blockquote>
&lt;p>Tokens should be created via a &lt;a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/">web flow&lt;/a>. An application sends users to GitHub to log in. GitHub then presents a dialog indicating the name of the app, as well as the level of access the app has once it&amp;rsquo;s authorized by the user. After a user authorizes access, GitHub redirects the user back to the application.&lt;/p>
&lt;/blockquote>
&lt;p>Note: I am going to use what they call &amp;ldquo;web flow&amp;rdquo; because seems to be the recommended method and some other methods will be deprecated by the end of this year: &lt;a href="https://developer.github.com/changes/2020-02-14-deprecating-oauth-auth-endpoint/">https://developer.github.com/changes/2020-02-14-deprecating-oauth-auth-endpoint/&lt;/a>&lt;/p>
&lt;h2 id="example-step-by-step">Example (Step by step)&lt;/h2>
&lt;p>In this example, I will write a simple web application that displays the list of all public repos and all their pull request of a user by following three steps.&lt;/p>
&lt;p>Summary:&lt;/p>
&lt;ol>
&lt;li>Authenticate Github API via &lt;a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/">web flow&lt;/a>&lt;/li>
&lt;li>Get repos of the authenticated user&lt;/li>
&lt;li>Get pull requests per each repo&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://cesarkina.com/blog/github-api-oauth-app/images/0.png" alt="">&lt;/p>
&lt;p>Details:&lt;/p>
&lt;ol>
&lt;li>Authenticate Github API via &lt;a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/">web flow&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>Follow these steps to create an oauth app: &lt;a href="https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/">https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/&lt;/a>
More information about oauth apps: &lt;a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/">https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/&lt;/a>&lt;/p>
&lt;p>In this example, this is the information I have:&lt;/p>
&lt;ul>
&lt;li>Application name: OAuth App Local&lt;/li>
&lt;li>Homepage URL: http://localhost:8888&lt;/li>
&lt;li>Application description: Just left in blank&lt;/li>
&lt;li>Authorization callback URL: http://localhost:8888&lt;/li>
&lt;/ul>
&lt;p>Then I created a simple &lt;code>index.html&lt;/code> file with a single link that points to &lt;code>https://github.com/login/oauth/authorize?scope=user:email,read:org&amp;amp;client_id=&amp;lt;CLIENT_ID&amp;gt;&lt;/code> . Notice &lt;code>&amp;lt;CLIENT_ID&amp;gt;&lt;/code> in the URL is the client id that Github gives you when you create your OAuth App. This can be in your public code. However, later in this article we will use also the &lt;code>Client Secret&lt;/code>, this SHOULD BE totally hidden from public access, meaning: it MUST BE in your backend layer.&lt;/p>
&lt;blockquote>
&lt;p>Note: We are asking for the following &amp;ldquo;permissions&amp;rdquo;: &lt;code>user:email&lt;/code> and &lt;code>read:org&lt;/code>. Ref: &lt;a href="https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/">https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Once the user is authenticated through Github Login Page, Github will redirect to the &amp;ldquo;callback URL&amp;rdquo; including a &lt;code>code&lt;/code> as query param like: &lt;code>http://localhost:8888?code=xxxxxx&lt;/code>. This &lt;code>code&lt;/code> will be used to make another call to get the access code.&lt;/p>
&lt;p>The public site makes a call to the Netlify Function located in the same project under the path &lt;code>/.netlify/functions/github-client?code=&amp;lt;CODE&amp;gt;&lt;/code>. Where &lt;code>&amp;lt;CODE&amp;gt;&lt;/code> is what Github had sent to the callback url in the previous step.&lt;/p>
&lt;p>The Netlify Function will make a call to &lt;code>https://github.com/login/oauth/access_token&lt;/code> sending:&lt;/p>
&lt;ul>
&lt;li>Client Secret (Environment Variable)&lt;/li>
&lt;li>Client ID (Environment Variable)&lt;/li>
&lt;li>Code (Query Param)&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Note: For more information about Netlify Functions, read this: &lt;a href="https://cesarkina.com/blog/netlify-function/">https://cesarkina.com/blog/netlify-function/&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Once you get the token from the endpoint above, then you will be able to make calls to any Github API that your token has access, in this case, we asked for the following scopes: &lt;code>user:email&lt;/code> and &lt;code>read:org&lt;/code> and all public scopes that don&amp;rsquo;t need to be specified for authorization.&lt;/p>
&lt;ol start="2">
&lt;li>Get repos of the authenticated user through &lt;code>GET /user/repos&lt;/code> endpoint . Ref: &lt;a href="https://developer.github.com/v3/repos/#list-repositories-for-the-authenticated-user">https://developer.github.com/v3/repos/#list-repositories-for-the-authenticated-user&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>Now that we have available the access token, we can make calls from the Front End to the Github API.&lt;/p>
&lt;pre>&lt;code class="language-js">const showRepos = async () =&amp;gt; {
fetch(&amp;quot;https://api.github.com/user/repos&amp;quot;, {
method: &amp;quot;GET&amp;quot;,
headers: {
Authorization: &amp;quot;Bearer &amp;quot; + localStorage.getItem(GH_ACCESS_TOKEN_KEY),
},
})
.then(function (response) {
return response.json()
})
.then(function (data) {
document.getElementById(&amp;quot;result&amp;quot;).innerHTML += &amp;quot;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;Repos&amp;quot;
data.forEach(function (repo, index) {
showReview(repo)
})
})
}
&lt;/code>&lt;/pre>
&lt;ol start="3">
&lt;li>Get pull requests per each repo retrieved from previous step through &lt;code>GET /repos/:owner/:repo/pulls&lt;/code> endpoint. Ref: &lt;a href="https://developer.github.com/v3/pulls/#list-pull-requests">https://developer.github.com/v3/pulls/#list-pull-requests&lt;/a>&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-js">const showReview = async repo =&amp;gt; {
fetch(&amp;quot;https://api.github.com/repos/&amp;quot; + repo.full_name + &amp;quot;/pulls?state=all&amp;quot;, {
method: &amp;quot;GET&amp;quot;,
headers: {
Authorization: &amp;quot;Bearer &amp;quot; + localStorage.getItem(GH_ACCESS_TOKEN_KEY),
},
})
.then(function (response) {
return response.json()
})
.then(function (data) {
document.getElementById(&amp;quot;result&amp;quot;).innerHTML +=
'&amp;lt;br/&amp;gt;&amp;lt;a href=&amp;quot;' +
repo.html_url +
'&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;' +
repo.full_name +
&amp;quot;&amp;lt;/a&amp;gt;&amp;quot;
data.forEach(function (pr, index) {
document.getElementById(&amp;quot;result&amp;quot;).innerHTML +=
'&amp;lt;br/&amp;gt;&amp;lt;a href=&amp;quot;' +
pr.html_url +
'&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;---- #' +
pr.number +
&amp;quot;: &amp;quot; +
pr.title +
&amp;quot; (&amp;quot; +
pr.state +
&amp;quot;)&amp;quot; +
&amp;quot;&amp;lt;/a&amp;gt;&amp;quot;
})
})
}
&lt;/code>&lt;/pre>
&lt;h2 id="test">Test&lt;/h2>
&lt;p>Login through web flow:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/github-api-oauth-app/images/1.png" alt="">&lt;/p>
&lt;p>Redirected to Github Login Page:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/github-api-oauth-app/images/2.png" alt="">&lt;/p>
&lt;p>You need to authorize this app:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/github-api-oauth-app/images/3.png" alt="">&lt;/p>
&lt;p>Final result:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/github-api-oauth-app/images/4.png" alt="">&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>I can see Github OAuth App web flow as standard way to authorize an app to read/write information on behalf of an authenticated user using an access tokens. Speaking particularly about Github, the only thing I am kind of dissapointed is the fact I can&amp;rsquo;t read Organization&amp;rsquo;s data without the permission of the owner, which make totally sense, but originally I thought it could be possible since I was able to access that kind of information via Personal Access Token, but seems like scope are pretty different and I should have read carefully the docs in the first place.&lt;/p>
&lt;p>I would like to create a more elaborated app that can show more information about repos, pull requests and stats using what I&amp;rsquo;ve learned here.&lt;/p>
&lt;p>Github repo: &lt;a href="https://github.com/ckinan/learning/tree/main/ckn-github-oauth-app">https://github.com/ckinan/learning/tree/main/ckn-github-oauth-app&lt;/a>&lt;/p>
&lt;h2 id="refs">Refs&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://developer.github.com/v3/guides/getting-started/#using-oauth-tokens-for-apps">https://developer.github.com/v3/guides/getting-started/#using-oauth-tokens-for-apps&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.github.com/v3/guides/basics-of-authentication/">https://developer.github.com/v3/guides/basics-of-authentication/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/">https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.github.com/v3/repos/">https://developer.github.com/v3/repos/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.github.com/v3/pulls/">https://developer.github.com/v3/pulls/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Netlify Functions</title><link>https://cesarkina.com/blog/netlify-function/</link><pubDate>Sat, 07 Mar 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/netlify-function/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>Exploring Netlify Functions: Concepts and some examples to get started with serverless functions.&lt;/p>
&lt;h2 id="motivation">Motivation&lt;/h2>
&lt;p>Sometimes I want to build simple web applications for demos or just for fun. These ideas relates interactions with APIs (free ones in most of the cases). However, even when these APIs have free plans, they force you to create an API KEY, which you don&amp;rsquo;t want to expose for security reasons.&lt;/p>
&lt;p>I don&amp;rsquo;t want to use secret keys in a single font end web application, because that approach would expose my keys. So, I have to handle those in a server side (backend apps). And here is where serverless functions caught my attention.&lt;/p>
&lt;p>There are some options I was checking to create and use serverless functions, but as I am already working with Netlify for this website, I wanted to give it a try and use &amp;ldquo;Netlify Functions&amp;rdquo;.&lt;/p>
&lt;h2 id="what-is-netlify-functions">What is &amp;ldquo;Netlify Functions&amp;rdquo;?&lt;/h2>
&lt;p>Serverless functions on AWS Lambda, handled by Netlify. Create, build, deploy and test these serverless functions on Netlify and locally with some great tools that Netlify provides.&lt;/p>
&lt;p>Currently, supported languages are:&lt;/p>
&lt;ul>
&lt;li>JavaScript&lt;/li>
&lt;li>Go&lt;/li>
&lt;/ul>
&lt;h2 id="concepts">Concepts&lt;/h2>
&lt;p>To get started with Netlify Functions, I&amp;rsquo;ve found some particulars to create, configure and serve your functions.&lt;/p>
&lt;ul>
&lt;li>netlify.toml (configuration file)&lt;/li>
&lt;li>Structure of a function (obviously)&lt;/li>
&lt;li>Commands to build and serve (the latter is optional if you want to deploy it locally)&lt;/li>
&lt;/ul>
&lt;p>Lets check some few details about them.&lt;/p>
&lt;h3 id="netlifytoml">netlify.toml&lt;/h3>
&lt;p>This file contains the configurations of your functions project. For this (very) small example, I am just specifying where the built functions are located (&amp;ldquo;dist&amp;rdquo; folder).&lt;/p>
&lt;p>Ref: &lt;a href="https://docs.netlify.com/configure-builds/file-based-configuration/">https://docs.netlify.com/configure-builds/file-based-configuration/&lt;/a>&lt;/p>
&lt;p>Example:&lt;/p>
&lt;pre>&lt;code class="language-toml">[build]
functions = &amp;quot;dist&amp;quot; # build output will be here
&lt;/code>&lt;/pre>
&lt;h3 id="structure-of-a-function">Structure of a function&lt;/h3>
&lt;p>From a &amp;ldquo;hello world&amp;rdquo; example found here: &lt;a href="https://functions-playground.netlify.com/">https://functions-playground.netlify.com/&lt;/a>&lt;/p>
&lt;pre>&lt;code class="language-js">exports.handler = function (event, context, callback) {
callback(null, {
statusCode: 200,
body: &amp;quot;Hello, World&amp;quot;,
})
}
&lt;/code>&lt;/pre>
&lt;p>Declare &lt;code>exports.handler&lt;/code> function with the following parameters:&lt;/p>
&lt;ul>
&lt;li>&lt;code>event&lt;/code> : Requests data&lt;/li>
&lt;li>&lt;code>context&lt;/code> : Context of the call. Example: &lt;a href="https://docs.netlify.com/functions/functions-and-identity/#access-identity-info-via-clientcontext">Functions with Netlify Identity&lt;/a>&lt;/li>
&lt;li>&lt;code>callback&lt;/code> : Creates reponses&lt;/li>
&lt;/ul>
&lt;h2 id="commands-to-build-and-serve">Commands to build and serve&lt;/h2>
&lt;ul>
&lt;li>&lt;code>netlify-lambda build&lt;/code> : Builds your function source code and place it into your built folder specified in the &lt;code>netlify.toml&lt;/code> configuration file in &lt;code>functions&lt;/code>. &lt;a href="https://github.com/netlify/netlify-lambda#netlify-lambda-build">Ref&lt;/a>&lt;/li>
&lt;li>&lt;code>netlify-lambda serve&lt;/code> : Runs your function locally. &lt;a href="https://github.com/netlify/netlify-lambda#netlify-lambda-serve-legacy-command">Ref&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="getting-started-example-step-by-step">Getting started (example step by step)&lt;/h2>
&lt;ol>
&lt;li>Initialize your project&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-bash">npm init
&lt;/code>&lt;/pre>
&lt;ol start="2">
&lt;li>Install &lt;code>netlify-lambda&lt;/code> command locally&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-bash">yarn add netlify-lambda
&lt;/code>&lt;/pre>
&lt;ol start="3">
&lt;li>Create the &lt;code>netlify.toml&lt;/code> file&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-toml">[build]
functions = &amp;quot;dist&amp;quot; # build output will be here
&lt;/code>&lt;/pre>
&lt;ol start="4">
&lt;li>Add &lt;code>build&lt;/code> and &lt;code>serve&lt;/code> scripts to &lt;code>package.json&lt;/code>. Note our source code folder is in &lt;code>./src&lt;/code> path.&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-js"> :
&amp;quot;scripts&amp;quot;: {
&amp;quot;build&amp;quot;: &amp;quot;netlify-lambda build src&amp;quot;,
&amp;quot;install&amp;quot;: &amp;quot;netlify-lambda install src&amp;quot;,
&amp;quot;serve&amp;quot;: &amp;quot;netlify-lambda serve src&amp;quot;
},
:
&lt;/code>&lt;/pre>
&lt;ol start="5">
&lt;li>Create your first function and name the file as &lt;code>hello-world.js&lt;/code>.&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-js">exports.handler = function (event, context, callback) {
callback(null, {
statusCode: 200,
body: &amp;quot;Hello, World&amp;quot;,
})
}
&lt;/code>&lt;/pre>
&lt;p>Your source tree should look like:&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/netlify-function/images/0.png" alt="">&lt;/p>
&lt;ol start="7">
&lt;li>Deploy&lt;/li>
&lt;/ol>
&lt;p>Two options: Locally and on Netlify&lt;/p>
&lt;p>7.1. Option 1: Locally&lt;/p>
&lt;pre>&lt;code class="language-js">npm run serve
&lt;/code>&lt;/pre>
&lt;p>You will see your port where your local function is exposed:&lt;/p>
&lt;pre>&lt;code>[0] ./hello-world.js 129 bytes {0} [built]
Lambda server is listening on 9000
&lt;/code>&lt;/pre>
&lt;p>Open in a browser: &lt;a href="http://localhost:9000/.netlify/functions/hello-world">http://localhost:9000/.netlify/functions/hello-world&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>Note: The path of the URL is: http://{host}:{port}/.netlify/functions/{your-function-name}) . UPDATE: I tried to change the &lt;code>.netlify/functions&lt;/code> path thing, but it was not possible. But, it is possible to redirect requests asking for your functions to whatever alias you want to have like &lt;code>/api&lt;/code> for instance. See: &lt;a href="https://docs.netlify.com/routing/redirects/#syntax-for-the-netlify-configuration-file">Netlify Routing Redirects&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>You will see the response of the function: &amp;ldquo;Hello, World&amp;rdquo;&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/netlify-function/images/1.png" alt="">&lt;/p>
&lt;p>7.2. Deploy on Netlify&lt;/p>
&lt;p>Commit and push your project into Github/Gitlab/Bitbucket, and deploy your project in Netlify, where the &amp;ldquo;Build Command&amp;rdquo; should be in this case: &lt;code>npm run install&lt;/code>. Once it got deployed, you can see the list of functions in Netlify under &lt;code>Project &amp;gt; Functions&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/netlify-function/images/2.png" alt="">&lt;/p>
&lt;p>Click on any of your functions, you will see the URL to invoke it. Example&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/netlify-function/images/3.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>Note: There are really good examples provided by Netlify Team of what you can do within a single function. I actually used them for this article. Link: &lt;a href="https://functions-playground.netlify.com/">https://functions-playground.netlify.com/&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 id="logging">Logging&lt;/h2>
&lt;p>Quoting docs: &lt;a href="https://docs.netlify.com/functions/logs/">https://docs.netlify.com/functions/logs/&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>Netlify displays a running log for the last hour of activity, including:&lt;/p>
&lt;ul>
&lt;li>Start of each invocation&lt;/li>
&lt;li>Any console.log() statements you include in your function code&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;h2 id="environment-variables">Environment variables&lt;/h2>
&lt;p>Following another example from &lt;a href="https://functions-playground.netlify.com/">https://functions-playground.netlify.com/&lt;/a> to get environment variables, create &lt;code>env.js&lt;/code> under &lt;code>src&lt;/code> folder.&lt;/p>
&lt;pre>&lt;code class="language-js">const GREETING = process.env.GREETING
exports.handler = async (event, context) =&amp;gt; {
return {
statusCode: 200,
body: GREETING,
}
}
&lt;/code>&lt;/pre>
&lt;p>Here, &lt;code>GREETING&lt;/code> is your environment variable. This is how you &amp;ldquo;hide&amp;rdquo; certain values you don&amp;rsquo;t want to share publicly to your clients behind a serverless function like this. Even if your source code is public in Github, you won&amp;rsquo;t need to have secret information in your code, but in environment variables like so.&lt;/p>
&lt;p>In the above example, if you are running:&lt;/p>
&lt;ul>
&lt;li>Locally: Follow instructions in this link &lt;a href="https://github.com/netlify/netlify-lambda/issues/118#issuecomment-506973346">https://github.com/netlify/netlify-lambda/issues/118#issuecomment-506973346&lt;/a>&lt;/li>
&lt;li>On Netlify: Just create an environment variable under &lt;code>Project &amp;gt; Settings &amp;gt; Build &amp;amp; Deploy &amp;gt; Environment&lt;/code> option.&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://cesarkina.com/blog/netlify-function/images/4.png" alt="">&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>Netlify Functions is definitely a great choice to get started with serverless functions and it will (for sure) help me to accomplish what I was looking for: to hide api secret keys to invoke third party&amp;rsquo;s APIs (more than likely I&amp;rsquo;ll reuse the &amp;ldquo;fetch.js&amp;rdquo; example).&lt;/p>
&lt;p>I am pretty sure there a lot more features Netlify provides within Functions behind the scenes happening that I am not aware of right now. But, I am happy with what I&amp;rsquo;ve just explored and learned about it, most importantly, Netlify is clear in their documentations and has a great community out there with super easy tutorials for beginners (like myself).&lt;/p>
&lt;p>Github repo: &lt;a href="https://github.com/ckinan/learning/tree/main/ckn-netlify-functions">https://github.com/ckinan/learning/tree/main/ckn-netlify-functions&lt;/a>&lt;/p>
&lt;p>EDIT: Here is another example using Netlify Functions and this free API to get the weather: &lt;a href="https://weatherstack.com/">https://weatherstack.com/&lt;/a> . Repo: &lt;a href="https://github.com/ckinan/learning/tree/main/ckn-netlify-weather">https://github.com/ckinan/learning/tree/main/ckn-netlify-weather&lt;/a>&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>Official docs: &lt;a href="https://docs.netlify.com/functions/overview/">https://docs.netlify.com/functions/overview/&lt;/a>&lt;/li>
&lt;li>&lt;code>netlify-lambda&lt;/code>: &lt;a href="https://github.com/netlify/netlify-lambda">https://github.com/netlify/netlify-lambda&lt;/a>&lt;/li>
&lt;li>Local environment variables &lt;a href="https://github.com/netlify/netlify-lambda/issues/118#issuecomment-506973346">https://github.com/netlify/netlify-lambda/issues/118#issuecomment-506973346&lt;/a>&lt;/li>
&lt;li>Great article to get started: &lt;a href="https://kentcdodds.com/blog/super-simple-start-to-serverless">https://kentcdodds.com/blog/super-simple-start-to-serverless&lt;/a>&lt;/li>
&lt;li>Another great article: &lt;a href="https://flaviocopes.com/netlify-functions/">https://flaviocopes.com/netlify-functions/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Github API - First Steps</title><link>https://cesarkina.com/blog/github-api-first-steps/</link><pubDate>Fri, 07 Feb 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/github-api-first-steps/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>First steps with Github API, looking at the official docs, executing some APIs and discovering what I can do with that.&lt;/p>
&lt;h2 id="motivation">Motivation&lt;/h2>
&lt;p>Currently, the company I am working with uses Github as git repository/platform. Code reviews, documentation, merges and other stuff going through there. It&amp;rsquo;s going great.&lt;/p>
&lt;p>I am curious about what else we can do using Github API, what pieces of the whole development process we can automate. So, this article is about Github API, what I know, what I learned, what I find useful.&lt;/p>
&lt;p>I may use the outcome of this (quick) research not only for this company, but anything else that may help other problems/opportunities that, right now, I can&amp;rsquo;t even imagine.&lt;/p>
&lt;h2 id="steps">Steps&lt;/h2>
&lt;p>This is the list of things this article/research will cover:&lt;/p>
&lt;ul>
&lt;li>Step #1: Take a look at their docs (overview)&lt;/li>
&lt;li>Step #2: Understand their authentication processes&lt;/li>
&lt;li>Step #3: Endpoints&lt;/li>
&lt;/ul>
&lt;h2 id="basics">Basics&lt;/h2>
&lt;p>Docs! Lets see how they explain their API in their documentation and see how easy (or complex) is to understand what we can achieve with it.&lt;/p>
&lt;p>Link: &lt;a href="https://developer.github.com/v3/guides/getting-started/">https://developer.github.com/v3/guides/getting-started/&lt;/a>&lt;/p>
&lt;p>This guide has very clear examples of how to call the API, like this one that doesn&amp;rsquo;t require any auth information and reads all public data from any github user using cURL:&lt;/p>
&lt;pre>&lt;code class="language-bash">$ curl https://api.github.com/users/ckinan
{
&amp;quot;login&amp;quot;: &amp;quot;ckinan&amp;quot;,
&amp;quot;id&amp;quot;: 7999613,
&amp;quot;node_id&amp;quot;: &amp;quot;MDQ6VXNlcjc5OTk2MTM=&amp;quot;,
&amp;quot;avatar_url&amp;quot;: &amp;quot;https://avatars1.githubusercontent.com/u/7999613?v=4&amp;quot;,
&amp;quot;gravatar_id&amp;quot;: &amp;quot;&amp;quot;,
&amp;quot;url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan&amp;quot;,
&amp;quot;html_url&amp;quot;: &amp;quot;https://github.com/ckinan&amp;quot;,
&amp;quot;followers_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/followers&amp;quot;,
&amp;quot;following_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/following{/other_user}&amp;quot;,
&amp;quot;gists_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/gists{/gist_id}&amp;quot;,
&amp;quot;starred_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/starred{/owner}{/repo}&amp;quot;,
&amp;quot;subscriptions_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/subscriptions&amp;quot;,
&amp;quot;organizations_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/orgs&amp;quot;,
&amp;quot;repos_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/repos&amp;quot;,
&amp;quot;events_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/events{/privacy}&amp;quot;,
&amp;quot;received_events_url&amp;quot;: &amp;quot;https://api.github.com/users/ckinan/received_events&amp;quot;,
&amp;quot;type&amp;quot;: &amp;quot;User&amp;quot;,
&amp;quot;site_admin&amp;quot;: false,
&amp;quot;name&amp;quot;: &amp;quot;Cesar Kina&amp;quot;,
&amp;quot;company&amp;quot;: null,
&amp;quot;blog&amp;quot;: &amp;quot;https://ckinan.com/&amp;quot;,
&amp;quot;location&amp;quot;: &amp;quot;Lima, Peru&amp;quot;,
&amp;quot;email&amp;quot;: null,
&amp;quot;hireable&amp;quot;: null,
&amp;quot;bio&amp;quot;: &amp;quot;software developer&amp;quot;,
&amp;quot;public_repos&amp;quot;: 22,
&amp;quot;public_gists&amp;quot;: 1,
&amp;quot;followers&amp;quot;: 3,
&amp;quot;following&amp;quot;: 7,
&amp;quot;created_at&amp;quot;: &amp;quot;2014-06-26T20:18:31Z&amp;quot;,
&amp;quot;updated_at&amp;quot;: &amp;quot;2020-02-08T00:08:38Z&amp;quot;
}
&lt;/code>&lt;/pre>
&lt;p>However, like any other API, it has a limited number of requests that any client can make to the API without being authenticated. Quoting:&lt;/p>
&lt;blockquote>
&lt;p>Unauthenticated clients can make 60 requests per hour. To get more requests per hour, we&amp;rsquo;ll need to authenticate. In fact, doing anything interesting with the GitHub API requires authentication.&lt;/p>
&lt;/blockquote>
&lt;h2 id="authentication-types">Authentication types&lt;/h2>
&lt;p>Basically, 2 options:&lt;/p>
&lt;p>&lt;strong>Option #1&lt;/strong>: Using personal access tokens: &lt;a href="https://developer.github.com/v3/guides/getting-started/#using-personal-access-tokens">https://developer.github.com/v3/guides/getting-started/#using-personal-access-tokens&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>The easiest and best way to authenticate with the GitHub API is by using Basic Authentication via OAuth tokens. OAuth tokens include personal access tokens.&lt;/p>
&lt;/blockquote>
&lt;p>Examples:&lt;/p>
&lt;pre>&lt;code class="language-bash">curl -i -u &amp;lt;your_username&amp;gt;:&amp;lt;your_access_token&amp;gt; https://api.github.com/user/repos
&lt;/code>&lt;/pre>
&lt;p>Provide privileges to your token to read your repos.&lt;/p>
&lt;p>&lt;strong>Option #2&lt;/strong>: Using OAuth tokens for apps : &lt;a href="https://developer.github.com/v3/guides/getting-started/#using-oauth-tokens-for-apps">https://developer.github.com/v3/guides/getting-started/#using-oauth-tokens-for-apps&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>Apps that need to read or write private information using the API on behalf of another user should use OAuth.&lt;/p>
&lt;/blockquote>
&lt;h2 id="endpoints">Endpoints&lt;/h2>
&lt;p>All the endpoints of the API in their documentation have examples of the calls, details of the input parameters and show how the return payload should look like, which is pretty good.&lt;/p>
&lt;p>Ref (overview of the API): &lt;a href="https://developer.github.com/v3/">https://developer.github.com/v3/&lt;/a>&lt;/p>
&lt;p>Docs give the endpoints paths like this (not the full url).&lt;/p>
&lt;p>Example of user repos: &lt;code>GET /user/repos&lt;/code>&lt;/p>
&lt;p>These are just 3 of the endpoints I tried out using a personal access token I&amp;rsquo;ve created:&lt;/p>
&lt;ul>
&lt;li>Get repos&lt;/li>
&lt;li>Get pull request&lt;/li>
&lt;li>List reviews of a pull request&lt;/li>
&lt;/ul>
&lt;p>Here more details about each of them:&lt;/p>
&lt;h3 id="get-repos">Get repos&lt;/h3>
&lt;p>Endpoint: &lt;code>GET /user/repos&lt;/code>&lt;/p>
&lt;p>Ref: &lt;a href="https://developer.github.com/v3/repos/#list-your-repositories">https://developer.github.com/v3/repos/#list-your-repositories&lt;/a>&lt;/p>
&lt;p>Example:&lt;/p>
&lt;pre>&lt;code class="language-bash">$ curl -i -u &amp;lt;your_username&amp;gt;:&amp;lt;your_access_token&amp;gt; https://api.github.com/user/repos
&lt;/code>&lt;/pre>
&lt;h3 id="get-pull-requests">Get pull requests&lt;/h3>
&lt;p>Endpoint: &lt;code>GET /repos/:owner/:repo/pulls&lt;/code>&lt;/p>
&lt;p>Ref: &lt;a href="https://developer.github.com/v3/pulls/#list-pull-requests">https://developer.github.com/v3/pulls/#list-pull-requests&lt;/a>&lt;/p>
&lt;pre>&lt;code class="language-bash">$ curl -i -u &amp;lt;your_username&amp;gt;:&amp;lt;your_access_token&amp;gt; https://api.github.com/repos/ckinan/ckinan.com/pulls?state=all
&lt;/code>&lt;/pre>
&lt;blockquote>
&lt;p>Parameters are also available, like &lt;code>state&lt;/code> in the example above to list &amp;ldquo;Either open, closed, or all to filter by state. Default: open&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;h3 id="list-reviews-of-a-pull-request">List reviews of a pull request&lt;/h3>
&lt;p>Endpoint: &lt;code>GET /repos/:owner/:repo/pulls/:pull_number/reviews&lt;/code>&lt;/p>
&lt;p>Ref: &lt;a href="https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request">https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request&lt;/a>&lt;/p>
&lt;pre>&lt;code class="language-bash">$ curl -i -u &amp;lt;your_username&amp;gt;:&amp;lt;your_access_token&amp;gt; https://api.github.com/repos/ckinan/ckinan.com/pulls/9/reviews
&lt;/code>&lt;/pre>
&lt;p>This is interesting, because you can see who submitted the review, whether this review refers to a comment only, request changes or approval.&lt;/p>
&lt;h2 id="final-thoughts">Final thoughts&lt;/h2>
&lt;p>Github has a great documentation, at least for a person who is obviously just trying to get used to the API (like me).&lt;/p>
&lt;p>For sure, using personal access token is easier to get used to the API. You just need to create one by going to your Settings and include your token in your calls. But, depending on the use case, it may be annoying to ask all users to create another token to make the application working, in that case, OAuth tokens for apps seems to be a better option, so that application can get authorization on behalf of the user who is logging in without explicitly creating a personal access token.&lt;/p>
&lt;p>I will have another space to continue this investigation, where I&amp;rsquo;d like to see more about OAuth tokens for apps, and play with the endpoint to read pull requests.&lt;/p></description></item><item><title>Auth0 - Simple Example with Vanilla JS</title><link>https://cesarkina.com/blog/auth0-simple-example-vanilla-js/</link><pubDate>Sun, 05 Jan 2020 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/auth0-simple-example-vanilla-js/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>First steps with Auth0 following their tutorial to create a simple login page with Vanilla JS.&lt;/p>
&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>Auth0 is a complete platfom that helps you to handle authentication and authorization services in your application. It provides SDKs to connect to their solution for many technologies such as Angular, Javascript, React Vue for SPA applications, not to mention Node, PHP, Python, Java (and others) for backend and others for mobile apps.&lt;/p>
&lt;p>This post is intended to get started with Auth0 by following the steps of the tutorial they have in their website using Vanilla JS (which is great and very detailed). However, after I completed all the steps, I decided to remove some parts of the code from the code, because all I wanted to have there was the most minimal version of this login page just to avoid being distracted by other things that are not directly related to the Auth0 SDK. During my research, I found this post which inspired me to do this work: &lt;a href="https://protoout.studio/posts/auth0-github-pages/">https://protoout.studio/posts/auth0-github-pages/&lt;/a> . So, thanks Protoout for this :)&lt;/p>
&lt;p>Quoting the goal of this tutorial:&lt;/p>
&lt;blockquote>
&lt;p>This tutorial demonstrates how to add user login to a Javascript application using Auth0.&lt;/p>
&lt;/blockquote>
&lt;p>You can find the tutorial here: &lt;a href="https://auth0.com/docs/quickstart/spa/vanillajs">https://auth0.com/docs/quickstart/spa/vanillajs&lt;/a>&lt;/p>
&lt;p>After executing all the steps in there I got this configuration in my first application I called &lt;code>auth0-javascript-tutorial&lt;/code>.&lt;/p>
&lt;h2 id="steps">Steps&lt;/h2>
&lt;h3 id="auth0-settings">Auth0 Settings&lt;/h3>
&lt;p>Domain and Client ID, you will need them later in your JS app.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/auth0-simple-example-vanilla-js/images/0.png" alt="">&lt;/p>
&lt;p>Configure the following:&lt;/p>
&lt;ul>
&lt;li>Allowed Callback URLs&lt;/li>
&lt;li>Allowed Web Origins&lt;/li>
&lt;li>Allowed Logout URLs&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>One Auth0-Application supports many Allowed Callback URLs, Web Origins and Logout URLs, so I have set only one application to accomplish multiple environments (local and github.io in my case).&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://cesarkina.com/blog/auth0-simple-example-vanilla-js/images/1.png" alt="">&lt;/p>
&lt;h3 id="indexhtml">index.html&lt;/h3>
&lt;p>Simple html file to show login/logout buttons and the &lt;code>gated-content&lt;/code> section where the logged user profile information should be displayed. No need to change anything here, keep it as the tutorial suggests it to be.&lt;/p>
&lt;blockquote>
&lt;p>You can load the &lt;code>auth0-spa-js&lt;/code> from CDN.&lt;/p>
&lt;/blockquote>
&lt;pre>&lt;code class="language-html">&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
&amp;lt;title&amp;gt;SPA SDK Sample&amp;lt;/title&amp;gt;
&amp;lt;style&amp;gt;
.hidden {
display: none;
}
label {
margin-bottom: 10px;
display: block;
}
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h2&amp;gt;SPA Authentication Sample&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;Welcome to our page!&amp;lt;/p&amp;gt;
&amp;lt;button id=&amp;quot;btn-login&amp;quot; disabled=&amp;quot;true&amp;quot; onclick=&amp;quot;login()&amp;quot;&amp;gt;Log in&amp;lt;/button&amp;gt;
&amp;lt;button id=&amp;quot;btn-logout&amp;quot; disabled=&amp;quot;true&amp;quot; onclick=&amp;quot;logout()&amp;quot;&amp;gt;Log out&amp;lt;/button&amp;gt;
&amp;lt;div class=&amp;quot;hidden&amp;quot; id=&amp;quot;gated-content&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;
You're seeing this content because you're currently
&amp;lt;strong&amp;gt;logged in&amp;lt;/strong&amp;gt;.
&amp;lt;/p&amp;gt;
&amp;lt;label&amp;gt;
Access token:
&amp;lt;pre id=&amp;quot;ipt-access-token&amp;quot;&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/label&amp;gt;
&amp;lt;label&amp;gt;
User profile:
&amp;lt;pre id=&amp;quot;ipt-user-profile&amp;quot;&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;script src=&amp;quot;https://cdn.auth0.com/js/auth0-spa-js/1.2/auth0-spa-js.production.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;app.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code>&lt;/pre>
&lt;h3 id="appjs">app.js&lt;/h3>
&lt;p>This is how my minimal version of the &lt;code>app.js&lt;/code> looks like:&lt;/p>
&lt;ul>
&lt;li>I am not serving the Domain and Client ID from any backend service (as the tutorial does serving them using Express).&lt;/li>
&lt;li>Just including them in the client as I don&amp;rsquo;t care about them being exposed publicly for now.&lt;/li>
&lt;li>Then I organized the &lt;code>onload()&lt;/code> function just in three steps: Configure the Client, Process the Login State and Update the UI.&lt;/li>
&lt;li>I made sure I didn&amp;rsquo;t miss any Auth0 function from the SDK that was originally used in the tutorial, so that I can check them out later.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-js">let auth0 = null
window.onload = async () =&amp;gt; {
await configureClient()
await processLoginState()
updateUI()
}
const configureClient = async () =&amp;gt; {
auth0 = await createAuth0Client({
domain: &amp;quot;dev-scp6vo-0.auth0.com&amp;quot;,
client_id: &amp;quot;7uwc6i4z1Xxn9JpWaz2NU1bc9uq4oBPw&amp;quot;,
})
}
const processLoginState = async () =&amp;gt; {
// Check code and state parameters
const query = window.location.search
if (query.includes(&amp;quot;code=&amp;quot;) &amp;amp;&amp;amp; query.includes(&amp;quot;state=&amp;quot;)) {
// Process the login state
await auth0.handleRedirectCallback()
// Use replaceState to redirect the user away and remove the querystring parameters
window.history.replaceState({}, document.title, window.location.pathname)
}
}
const updateUI = async () =&amp;gt; {
const isAuthenticated = await auth0.isAuthenticated()
document.getElementById(&amp;quot;btn-logout&amp;quot;).disabled = !isAuthenticated
document.getElementById(&amp;quot;btn-login&amp;quot;).disabled = isAuthenticated
// NEW - add logic to show/hide gated content after authentication
if (isAuthenticated) {
document.getElementById(&amp;quot;gated-content&amp;quot;).classList.remove(&amp;quot;hidden&amp;quot;)
document.getElementById(
&amp;quot;ipt-access-token&amp;quot;
).innerHTML = await auth0.getTokenSilently()
document.getElementById(&amp;quot;ipt-user-profile&amp;quot;).innerHTML = JSON.stringify(
await auth0.getUser()
)
} else {
document.getElementById(&amp;quot;gated-content&amp;quot;).classList.add(&amp;quot;hidden&amp;quot;)
}
}
const login = async () =&amp;gt; {
await auth0.loginWithRedirect({
redirect_uri: window.location.href,
})
}
const logout = () =&amp;gt; {
auth0.logout({
returnTo: window.location.href,
})
}
&lt;/code>&lt;/pre>
&lt;p>The above code snippet is a minified version of what Auth0 has prepared in their documentation. Basically, it establishes connection with the Application I created in their platform, then do some checks with the state of the authentication process to show or hide information about the user who is (or not) logged in. Below is the list of classes &amp;amp; methods that stricly come from the Auth0 SDK:&lt;/p>
&lt;ul>
&lt;li>createAuth0Client&lt;/li>
&lt;li>handleRedirectCallback&lt;/li>
&lt;li>isAuthenticated&lt;/li>
&lt;li>getTokenSilently&lt;/li>
&lt;li>getUser&lt;/li>
&lt;li>loginWithRedirect&lt;/li>
&lt;li>logout&lt;/li>
&lt;/ul>
&lt;p>This exercise helped me to have a better understanding what actually this simple login page is doing through the SDK. Also, the SDK documentation has good content about each class/function/property involved here: &lt;a href="https://auth0.github.io/auth0-spa-js/">https://auth0.github.io/auth0-spa-js/&lt;/a>&lt;/p>
&lt;p>One key point in &lt;code>app.js&lt;/code> is this line:&lt;/p>
&lt;pre>&lt;code class="language-js">await auth0.handleRedirectCallback()
&lt;/code>&lt;/pre>
&lt;p>According to the SDK documentation:&lt;/p>
&lt;blockquote>
&lt;p>After the browser redirects back to the callback page, call handleRedirectCallback to handle success and error responses from Auth0. If the response is successful, results will be valid according to their expiration times.&lt;/p>
&lt;/blockquote>
&lt;p>Whatever happens with the authentication process won&amp;rsquo;t be effectively readable in your application until the call to &lt;code>auth0.handleRedirectCallback();&lt;/code>, you won&amp;rsquo;t be even able to get the actual state of the login process result. For example: &lt;code>auth0.isAuthenticated();&lt;/code> would not get &lt;code>true&lt;/code> after a successful login if it was not handled previously by the &lt;code>auth0.handleRedirectCallback();&lt;/code> function.&lt;/p>
&lt;h2 id="test">Test&lt;/h2>
&lt;p>Open &lt;a href="https://ckinan.github.io/auth0-javascript-tutorial/">https://ckinan.github.io/auth0-javascript-tutorial/&lt;/a> and click on the &amp;ldquo;Login in&amp;rdquo; button.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/auth0-simple-example-vanilla-js/images/2.png" alt="">&lt;/p>
&lt;p>You will be redirected to Auth0 Authentication page where you can login to this app using your Google or Github account. By the way, the free plan allows you to set up to two social connections.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/auth0-simple-example-vanilla-js/images/3.png" alt="">&lt;/p>
&lt;p>After login success, it will redirect to your page and the user information will be displayed.&lt;/p>
&lt;p>&lt;img src="https://cesarkina.com/blog/auth0-simple-example-vanilla-js/images/4.png" alt="">&lt;/p>
&lt;h2 id="conclusions">Conclusions&lt;/h2>
&lt;p>Auth0 is a great option to handle Authentication and Authorization processes. Their tutorial is very detailed and easy to follow (didn&amp;rsquo;t have much problems). However, in order to focus on what really matters to me in their SDK, I had to remove/debug/reinsert some lines of code. I am excited about building a more real use case using Auth0 and see more features of it.&lt;/p>
&lt;h2 id="links">Links&lt;/h2>
&lt;ul>
&lt;li>Auth0 Vanilla JS Tutorial: &lt;a href="https://auth0.com/docs/quickstart/spa/vanillajs">https://auth0.com/docs/quickstart/spa/vanillajs&lt;/a>&lt;/li>
&lt;li>Auth0 SDK for Single Page Applications: &lt;a href="https://auth0.github.io/auth0-spa-js/">https://auth0.github.io/auth0-spa-js/&lt;/a>&lt;/li>
&lt;li>Test: &lt;a href="https://ckinan.github.io/auth0-javascript-tutorial/">https://ckinan.github.io/auth0-javascript-tutorial/&lt;/a>&lt;/li>
&lt;li>Repo: &lt;a href="https://github.com/ckinan/learning/tree/main/auth0-javascript-tutorial">https://github.com/ckinan/learning/tree/main/auth0-javascript-tutorial&lt;/a>&lt;/li>
&lt;li>Great tutorial post: &lt;a href="https://protoout.studio/posts/auth0-github-pages/">https://protoout.studio/posts/auth0-github-pages/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Intro</title><link>https://cesarkina.com/blog/intro/</link><pubDate>Tue, 08 Oct 2019 00:00:00 +0000</pubDate><guid>https://cesarkina.com/blog/intro/</guid><author>cesar@cesarkina.com (Cesar Kina)</author><description>&lt;p>This is the first article of this website. As software engineer, I would be writing stuff related to that field (mmhh or maybe not). For now, I just want to mention the awesome tools I found to build this website.&lt;/p>
&lt;h2 id="static-site-generator">Static site generator&lt;/h2>
&lt;ul>
&lt;li>Gatsby&lt;/li>
&lt;li>UPDATE December 2019: Moved to Docusaurus&lt;/li>
&lt;li>UPDATE April 2020: Came back to Gatsby&lt;/li>
&lt;li>UPDATE January 2021: Moved to &lt;a href="https://cesarkina.com/blog/my-own-static-site-generator/">my own static site generator&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="hosting">Hosting&lt;/h2>
&lt;ul>
&lt;li>Netlify&lt;/li>
&lt;li>UPDATE January 2021: Moved to Digital Ocean&lt;/li>
&lt;li>UPDATE early 2021: Moved to Netlify&lt;/li>
&lt;li>UPDATE June 2021: Moved to Firebase&lt;/li>
&lt;li>UPDATE January 2022: Setup Github Actions for site deployment&lt;/li>
&lt;/ul>
&lt;h2 id="styles">Styles&lt;/h2>
&lt;ul>
&lt;li>Plain CSS&lt;/li>
&lt;li>UPDATE April 2020: Moved to Tailwind CSS&lt;/li>
&lt;li>UPDATE January 2021: Plain CSS again&lt;/li>
&lt;li>UPDATE July 2021: Dark theme, color palette from &lt;a href="https://www.nordtheme.com/">Nord Theme&lt;/a>&lt;/li>
&lt;li>UPDATE January 2022: Go back to light theme + High contrast&lt;/li>
&lt;/ul>
&lt;h2 id="final-notes">Final Notes&lt;/h2>
&lt;p>I am totally new to these technologies, so I might not be using them properly or not aware of all the features they
provide. I simply wanted to start writing, so for now I&amp;rsquo;ll be using platforms and technologies built by more experienced
people, then I&amp;rsquo;ll try to build this site from scratch by my own.&lt;/p>
&lt;ul>
&lt;li>UPDATE Jan 2021: I decided to use this post to mention changes I&amp;rsquo;m doing to this site.&lt;/li>
&lt;/ul></description></item></channel></rss>