lens @ fbcac585cf626917e2baf1d0065c7b632341ba01

ref: Move auth functions to service
  1diff --git a/pkg/ext/auth.go b/pkg/ext/auth.go
  2deleted file mode 100644
  3index ed122bb6b2631d882efac0f6191fe3c29e0dd050..0000000000000000000000000000000000000000
  4--- a/pkg/ext/auth.go
  5+++ /dev/null
  6@@ -1,72 +0,0 @@
  7-package ext
  8-
  9-import (
 10-	"bytes"
 11-	"crypto/aes"
 12-	"crypto/cipher"
 13-	"crypto/rand"
 14-	"encoding/gob"
 15-	"errors"
 16-	"io"
 17-)
 18-
 19-type Token struct {
 20-	UserID   uint
 21-	Username string
 22-}
 23-
 24-func ReadToken(data []byte, key []byte) (*Token, error) {
 25-	block, err := aes.NewCipher(key)
 26-	if err != nil {
 27-		return nil, err
 28-	}
 29-
 30-	aesgcm, err := cipher.NewGCM(block)
 31-	if err != nil {
 32-		panic(err.Error())
 33-	}
 34-
 35-	nonceSize := aesgcm.NonceSize()
 36-	if len(data) < nonceSize {
 37-		return nil, errors.New("nonce size greater than data's size")
 38-	}
 39-
 40-	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
 41-	plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
 42-	if err != nil {
 43-		return nil, err
 44-	}
 45-
 46-	r := bytes.NewReader(plaintext)
 47-	var token Token
 48-	dec := gob.NewDecoder(r)
 49-	if err = dec.Decode(&token); err != nil {
 50-		return nil, err
 51-	}
 52-	return &token, nil
 53-}
 54-
 55-func WriteToken(token *Token, key []byte) ([]byte, error) {
 56-	block, err := aes.NewCipher(key)
 57-	if err != nil {
 58-		return nil, err
 59-	}
 60-
 61-	aesgcm, err := cipher.NewGCM(block)
 62-	if err != nil {
 63-		return nil, err
 64-	}
 65-
 66-	var buffer bytes.Buffer
 67-	enc := gob.NewEncoder(&buffer)
 68-	if err := enc.Encode(token); err != nil {
 69-		return nil, err
 70-	}
 71-	nonce := make([]byte, aesgcm.NonceSize())
 72-	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
 73-		return nil, err
 74-	}
 75-
 76-	ciphertext := aesgcm.Seal(nonce, nonce, buffer.Bytes(), nil)
 77-	return ciphertext, nil
 78-}
 79diff --git a/pkg/ext/auth_test.go b/pkg/ext/auth_test.go
 80deleted file mode 100644
 81index dc72a0cd66d92a1a07de9ee36a08120d954f6e60..0000000000000000000000000000000000000000
 82--- a/pkg/ext/auth_test.go
 83+++ /dev/null
 84@@ -1,40 +0,0 @@
 85-//go:build unit
 86-
 87-package ext
 88-
 89-import (
 90-	"testing"
 91-
 92-	"git.sr.ht/~gabrielgio/img/pkg/testkit"
 93-)
 94-
 95-func TestReadWriteToken(t *testing.T) {
 96-	t.Parallel()
 97-
 98-	testCases := []struct {
 99-		name  string
100-		key   []byte
101-		token *Token
102-	}{
103-		{
104-			name: "Normal write",
105-			key:  []byte("AES256Key-32Characters1234567890"),
106-			token: &Token{
107-				UserID:   3,
108-				Username: "username",
109-			},
110-		},
111-	}
112-
113-	for _, tc := range testCases {
114-		t.Run(tc.name, func(t *testing.T) {
115-			data, err := WriteToken(tc.token, tc.key)
116-			testkit.TestFatalError(t, "WriteToken", err)
117-
118-			token, err := ReadToken(data, tc.key)
119-			testkit.TestFatalError(t, "ReadToken", err)
120-
121-			testkit.TestValue(t, "ReadWriteToken", token, tc.token)
122-		})
123-	}
124-}
125diff --git a/pkg/ext/middleware.go b/pkg/ext/middleware.go
126index 2dd1cca57f49e61df5123bdb61fa71dd6439d99d..bcc6c5feb3fa1e30f54afc64d96dfc2c6ad5ca2d 100644
127--- a/pkg/ext/middleware.go
128+++ b/pkg/ext/middleware.go
129@@ -8,6 +8,7 @@ 	"github.com/sirupsen/logrus"
130 	"github.com/valyala/fasthttp"
131 
132 	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
133+	"git.sr.ht/~gabrielgio/img/pkg/service"
134 )
135 
136 func HTML(next fasthttp.RequestHandler) fasthttp.RequestHandler {
137@@ -77,7 +78,7 @@ 			a.entry.Error(err)
138 			return
139 		}
140 
141-		token, err := ReadToken(auth, a.key)
142+		token, err := service.ReadToken(auth, a.key)
143 		if err != nil {
144 			a.entry.Error(err)
145 			ctx.Redirect(redirectLogin, 307)
146@@ -92,9 +93,9 @@ 		next(ctx)
147 	}
148 }
149 
150-func GetTokenFromCtx(ctx *fasthttp.RequestCtx) *Token {
151+func GetTokenFromCtx(ctx *fasthttp.RequestCtx) *service.Token {
152 	tokenValue := ctx.UserValue("token")
153-	if token, ok := tokenValue.(*Token); ok {
154+	if token, ok := tokenValue.(*service.Token); ok {
155 		return token
156 	}
157 	return nil
158@@ -113,7 +114,7 @@
159 func (i *InitialSetupMiddleware) Check(next fasthttp.RequestHandler) fasthttp.RequestHandler {
160 	return func(ctx *fasthttp.RequestCtx) {
161 		// if user has been set to context it is logged in already
162-		_, ok := ctx.UserValue("token").(*Token)
163+		_, ok := ctx.UserValue("token").(*service.Token)
164 		if ok {
165 			next(ctx)
166 			return
167diff --git a/pkg/service/auth.go b/pkg/service/auth.go
168index 761c70b4e0a416d0f556bb929318f1e1170f11c4..1966e702241f86248aeb5101a1d2463bfa280a01 100644
169--- a/pkg/service/auth.go
170+++ b/pkg/service/auth.go
171@@ -1,12 +1,18 @@
172 package service
173 
174 import (
175+	"bytes"
176 	"context"
177+	"crypto/aes"
178+	"crypto/cipher"
179+	"crypto/rand"
180+	"encoding/gob"
181+	"errors"
182+	"io"
183 
184 	"golang.org/x/crypto/bcrypt"
185 
186 	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
187-	"git.sr.ht/~gabrielgio/img/pkg/ext"
188 )
189 
190 type AuthController struct {
191@@ -42,11 +48,11 @@ 	if err := bcrypt.CompareHashAndPassword(hashedPassword, password); err != nil {
192 		return nil, err
193 	}
194 
195-	token := &ext.Token{
196+	token := &Token{
197 		UserID:   id,
198 		Username: string(username),
199 	}
200-	return ext.WriteToken(token, c.key)
201+	return WriteToken(token, c.key)
202 }
203 
204 // InitialRegister register a initial user, it will validate if there is another
205@@ -75,3 +81,64 @@ 	})
206 
207 	return err
208 }
209+
210+type Token struct {
211+	UserID   uint
212+	Username string
213+}
214+
215+func ReadToken(data []byte, key []byte) (*Token, error) {
216+	block, err := aes.NewCipher(key)
217+	if err != nil {
218+		return nil, err
219+	}
220+
221+	aesgcm, err := cipher.NewGCM(block)
222+	if err != nil {
223+		panic(err.Error())
224+	}
225+
226+	nonceSize := aesgcm.NonceSize()
227+	if len(data) < nonceSize {
228+		return nil, errors.New("nonce size greater than data's size")
229+	}
230+
231+	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
232+	plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
233+	if err != nil {
234+		return nil, err
235+	}
236+
237+	r := bytes.NewReader(plaintext)
238+	var token Token
239+	dec := gob.NewDecoder(r)
240+	if err = dec.Decode(&token); err != nil {
241+		return nil, err
242+	}
243+	return &token, nil
244+}
245+
246+func WriteToken(token *Token, key []byte) ([]byte, error) {
247+	block, err := aes.NewCipher(key)
248+	if err != nil {
249+		return nil, err
250+	}
251+
252+	aesgcm, err := cipher.NewGCM(block)
253+	if err != nil {
254+		return nil, err
255+	}
256+
257+	var buffer bytes.Buffer
258+	enc := gob.NewEncoder(&buffer)
259+	if err := enc.Encode(token); err != nil {
260+		return nil, err
261+	}
262+	nonce := make([]byte, aesgcm.NonceSize())
263+	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
264+		return nil, err
265+	}
266+
267+	ciphertext := aesgcm.Seal(nonce, nonce, buffer.Bytes(), nil)
268+	return ciphertext, nil
269+}
270diff --git a/pkg/service/auth_test.go b/pkg/service/auth_test.go
271index 35b247575a785ddb5db5c0df0b810c2b6ec08fd8..7083d0c90ea75569f4e2532f86228a57c49df253 100644
272--- a/pkg/service/auth_test.go
273+++ b/pkg/service/auth_test.go
274@@ -64,7 +64,7 @@
275 			auth, err := scene.controller.Login(scene.ctx, []byte(tc.username), tc.password)
276 			testkit.TestFatalError(t, "Login", err)
277 
278-			token, err := ext.ReadToken(auth, key)
279+			token, err := ReadToken(auth, key)
280 			testkit.TestFatalError(t, "Login", err)
281 
282 			testkit.TestValue(t, "Login", tc.username, token.Username)
283@@ -76,3 +76,34 @@
284 func remove[T any](slice []T, s int) []T {
285 	return append(slice[:s], slice[s+1:]...)
286 }
287+
288+func TestReadWriteToken(t *testing.T) {
289+	t.Parallel()
290+
291+	testCases := []struct {
292+		name  string
293+		key   []byte
294+		token *Token
295+	}{
296+		{
297+			name: "Normal write",
298+			key:  []byte("AES256Key-32Characters1234567890"),
299+			token: &Token{
300+				UserID:   3,
301+				Username: "username",
302+			},
303+		},
304+	}
305+
306+	for _, tc := range testCases {
307+		t.Run(tc.name, func(t *testing.T) {
308+			data, err := WriteToken(tc.token, tc.key)
309+			testkit.TestFatalError(t, "WriteToken", err)
310+
311+			token, err := ReadToken(data, tc.key)
312+			testkit.TestFatalError(t, "ReadToken", err)
313+
314+			testkit.TestValue(t, "ReadWriteToken", token, tc.token)
315+		})
316+	}
317+}