From 5be7095e879666e5ae8e45b782eda481fcfab176 Mon Sep 17 00:00:00 2001
From: Hector <hector@hjs.dev>
Date: Thu, 13 Jan 2022 22:21:39 +0000
Subject: [PATCH] refactor config basic auth to handle hashing data

Add unit tests for the basicAuth to ensure it behaves as expected.
Update the basic auth provider struct in the cfg package to handle all the
hashing automatically.
---
 src/auth/middleware.go    |  4 +--
 src/cfg/basicAuth.go      | 17 +++++++++--
 src/cfg/basicAuth_test.go | 60 +++++++++++++++++++++++++++++++++++++++
 src/cfg/cfg.go            |  8 ++----
 4 files changed, 77 insertions(+), 12 deletions(-)
 create mode 100644 src/cfg/basicAuth_test.go

diff --git a/src/auth/middleware.go b/src/auth/middleware.go
index 42e818a..2518932 100644
--- a/src/auth/middleware.go
+++ b/src/auth/middleware.go
@@ -25,9 +25,7 @@ func BasicAuthMiddleware(handlerFunc http.HandlerFunc, basicAuthProvider BasicAu
 func doesBasicAuthMatch(r *http.Request, basicAuthProvider BasicAuthProvider) bool {
 	rawUsername, rawPassword, ok := r.BasicAuth()
 	if ok {
-		username := HashString(rawUsername)
-		password := HashString(rawPassword)
-		return basicAuthProvider.DoesBasicAuthMatch(username, password)
+		return basicAuthProvider.DoesBasicAuthMatch(rawUsername, rawPassword)
 	}
 	return false
 }
diff --git a/src/cfg/basicAuth.go b/src/cfg/basicAuth.go
index c79dc8c..bc5408f 100644
--- a/src/cfg/basicAuth.go
+++ b/src/cfg/basicAuth.go
@@ -1,14 +1,25 @@
 package cfg
 
-type basicAuth struct {
+import "fail2ban-prometheus-exporter/auth"
+
+type hashedBasicAuth struct {
 	username string
 	password string
 }
 
-func (p *basicAuth) Enabled() bool {
+func newHashedBasicAuth(rawUsername, rawPassword string) *hashedBasicAuth {
+	return &hashedBasicAuth{
+		username: auth.HashString(rawUsername),
+		password: auth.HashString(rawPassword),
+	}
+}
+
+func (p *hashedBasicAuth) Enabled() bool {
 	return len(p.username) > 0 && len(p.password) > 0
 }
 
-func (p *basicAuth) DoesBasicAuthMatch(username, password string) bool {
+func (p *hashedBasicAuth) DoesBasicAuthMatch(rawUsername, rawPassword string) bool {
+	username := auth.HashString(rawUsername)
+	password := auth.HashString(rawPassword)
 	return username == p.username && password == p.password
 }
diff --git a/src/cfg/basicAuth_test.go b/src/cfg/basicAuth_test.go
new file mode 100644
index 0000000..85bd8b3
--- /dev/null
+++ b/src/cfg/basicAuth_test.go
@@ -0,0 +1,60 @@
+package cfg
+
+import "testing"
+
+func Test_hashedBasicAuth_DoesBasicAuthMatch(t *testing.T) {
+	type args struct {
+		username string
+		password string
+	}
+	type fields struct {
+		username string
+		password string
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		args   args
+		want   bool
+	}{
+		{"Happy test #1", fields{username: "1234", password: "1234"}, args{username: "1234", password: "1234"}, true},
+		{"Happy test #2", fields{username: "test", password: "1234"}, args{username: "test", password: "1234"}, true},
+		{"Happy test #3", fields{username: "TEST", password: "1234"}, args{username: "TEST", password: "1234"}, true},
+		{"Non match #1", fields{username: "test", password: "1234"}, args{username: "1234", password: "1234"}, false},
+		{"Non match #2", fields{username: "1234", password: "test"}, args{username: "1234", password: "1234"}, false},
+		{"Non match #3", fields{username: "1234", password: "test"}, args{username: "1234", password: "TEST"}, false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			basicAuth := newHashedBasicAuth(tt.fields.username, tt.fields.password)
+			if got := basicAuth.DoesBasicAuthMatch(tt.args.username, tt.args.password); got != tt.want {
+				t.Errorf("DoesBasicAuthMatch() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_hashedBasicAuth_Enabled(t *testing.T) {
+	type fields struct {
+		username string
+		password string
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   bool
+	}{
+		{"Both blank", fields{username: "", password: ""}, false},
+		{"Single blank #1", fields{username: "test", password: ""}, false},
+		{"Single blank #1", fields{username: "", password: "test"}, false},
+		{"Both populated", fields{username: "test", password: "test"}, true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			basicAuth := newHashedBasicAuth(tt.fields.username, tt.fields.password)
+			if got := basicAuth.Enabled(); got != tt.want {
+				t.Errorf("Enabled() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/src/cfg/cfg.go b/src/cfg/cfg.go
index 0b33b99..008fef1 100644
--- a/src/cfg/cfg.go
+++ b/src/cfg/cfg.go
@@ -1,7 +1,6 @@
 package cfg
 
 import (
-	"fail2ban-prometheus-exporter/auth"
 	"flag"
 	"fmt"
 	"os"
@@ -19,7 +18,7 @@ type AppSettings struct {
 	Fail2BanSocketPath   string
 	FileCollectorPath    string
 	FileCollectorEnabled bool
-	BasicAuthProvider    *basicAuth
+	BasicAuthProvider    *hashedBasicAuth
 }
 
 func Parse() *AppSettings {
@@ -43,10 +42,7 @@ func Parse() *AppSettings {
 }
 
 func (settings *AppSettings) setBasicAuthValues(rawUsername, rawPassword string) {
-	settings.BasicAuthProvider = &basicAuth{
-		username: auth.HashString(rawUsername),
-		password: auth.HashString(rawPassword),
-	}
+	settings.BasicAuthProvider = newHashedBasicAuth(rawUsername, rawPassword)
 }
 
 func (settings *AppSettings) validateFlags() {