diff options
-rw-r--r-- | pkg/ext/auth.go | 72 | ||||
-rw-r--r-- | pkg/ext/auth_test.go | 40 | ||||
-rw-r--r-- | pkg/ext/middleware.go | 9 | ||||
-rw-r--r-- | pkg/service/auth.go | 73 | ||||
-rw-r--r-- | pkg/service/auth_test.go | 33 |
5 files changed, 107 insertions, 120 deletions
diff --git a/pkg/ext/auth.go b/pkg/ext/auth.go deleted file mode 100644 index ed122bb..0000000 --- a/pkg/ext/auth.go +++ /dev/null @@ -1,72 +0,0 @@ -package ext - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/gob" - "errors" - "io" -) - -type Token struct { - UserID uint - Username string -} - -func ReadToken(data []byte, key []byte) (*Token, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - panic(err.Error()) - } - - nonceSize := aesgcm.NonceSize() - if len(data) < nonceSize { - return nil, errors.New("nonce size greater than data's size") - } - - nonce, ciphertext := data[:nonceSize], data[nonceSize:] - plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - r := bytes.NewReader(plaintext) - var token Token - dec := gob.NewDecoder(r) - if err = dec.Decode(&token); err != nil { - return nil, err - } - return &token, nil -} - -func WriteToken(token *Token, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - var buffer bytes.Buffer - enc := gob.NewEncoder(&buffer) - if err := enc.Encode(token); err != nil { - return nil, err - } - nonce := make([]byte, aesgcm.NonceSize()) - if _, err = io.ReadFull(rand.Reader, nonce); err != nil { - return nil, err - } - - ciphertext := aesgcm.Seal(nonce, nonce, buffer.Bytes(), nil) - return ciphertext, nil -} diff --git a/pkg/ext/auth_test.go b/pkg/ext/auth_test.go deleted file mode 100644 index dc72a0c..0000000 --- a/pkg/ext/auth_test.go +++ /dev/null @@ -1,40 +0,0 @@ -//go:build unit - -package ext - -import ( - "testing" - - "git.sr.ht/~gabrielgio/img/pkg/testkit" -) - -func TestReadWriteToken(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - key []byte - token *Token - }{ - { - name: "Normal write", - key: []byte("AES256Key-32Characters1234567890"), - token: &Token{ - UserID: 3, - Username: "username", - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - data, err := WriteToken(tc.token, tc.key) - testkit.TestFatalError(t, "WriteToken", err) - - token, err := ReadToken(data, tc.key) - testkit.TestFatalError(t, "ReadToken", err) - - testkit.TestValue(t, "ReadWriteToken", token, tc.token) - }) - } -} diff --git a/pkg/ext/middleware.go b/pkg/ext/middleware.go index 2dd1cca..bcc6c5f 100644 --- a/pkg/ext/middleware.go +++ b/pkg/ext/middleware.go @@ -8,6 +8,7 @@ import ( "github.com/valyala/fasthttp" "git.sr.ht/~gabrielgio/img/pkg/database/repository" + "git.sr.ht/~gabrielgio/img/pkg/service" ) func HTML(next fasthttp.RequestHandler) fasthttp.RequestHandler { @@ -77,7 +78,7 @@ func (a *AuthMiddleware) LoggedIn(next fasthttp.RequestHandler) fasthttp.Request return } - token, err := ReadToken(auth, a.key) + token, err := service.ReadToken(auth, a.key) if err != nil { a.entry.Error(err) ctx.Redirect(redirectLogin, 307) @@ -92,9 +93,9 @@ func (a *AuthMiddleware) LoggedIn(next fasthttp.RequestHandler) fasthttp.Request } } -func GetTokenFromCtx(ctx *fasthttp.RequestCtx) *Token { +func GetTokenFromCtx(ctx *fasthttp.RequestCtx) *service.Token { tokenValue := ctx.UserValue("token") - if token, ok := tokenValue.(*Token); ok { + if token, ok := tokenValue.(*service.Token); ok { return token } return nil @@ -113,7 +114,7 @@ func NewInitialSetupMiddleware(userRepository repository.UserRepository) *Initia func (i *InitialSetupMiddleware) Check(next fasthttp.RequestHandler) fasthttp.RequestHandler { return func(ctx *fasthttp.RequestCtx) { // if user has been set to context it is logged in already - _, ok := ctx.UserValue("token").(*Token) + _, ok := ctx.UserValue("token").(*service.Token) if ok { next(ctx) return diff --git a/pkg/service/auth.go b/pkg/service/auth.go index 761c70b..1966e70 100644 --- a/pkg/service/auth.go +++ b/pkg/service/auth.go @@ -1,12 +1,18 @@ package service import ( + "bytes" "context" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/gob" + "errors" + "io" "golang.org/x/crypto/bcrypt" "git.sr.ht/~gabrielgio/img/pkg/database/repository" - "git.sr.ht/~gabrielgio/img/pkg/ext" ) type AuthController struct { @@ -42,11 +48,11 @@ func (c *AuthController) Login(ctx context.Context, username, password []byte) ( return nil, err } - token := &ext.Token{ + token := &Token{ UserID: id, Username: string(username), } - return ext.WriteToken(token, c.key) + return WriteToken(token, c.key) } // InitialRegister register a initial user, it will validate if there is another @@ -75,3 +81,64 @@ func (c *AuthController) InitialRegister(ctx context.Context, username, password return err } + +type Token struct { + UserID uint + Username string +} + +func ReadToken(data []byte, key []byte) (*Token, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + panic(err.Error()) + } + + nonceSize := aesgcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("nonce size greater than data's size") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + r := bytes.NewReader(plaintext) + var token Token + dec := gob.NewDecoder(r) + if err = dec.Decode(&token); err != nil { + return nil, err + } + return &token, nil +} + +func WriteToken(token *Token, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + var buffer bytes.Buffer + enc := gob.NewEncoder(&buffer) + if err := enc.Encode(token); err != nil { + return nil, err + } + nonce := make([]byte, aesgcm.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + ciphertext := aesgcm.Seal(nonce, nonce, buffer.Bytes(), nil) + return ciphertext, nil +} diff --git a/pkg/service/auth_test.go b/pkg/service/auth_test.go index 35b2475..7083d0c 100644 --- a/pkg/service/auth_test.go +++ b/pkg/service/auth_test.go @@ -64,7 +64,7 @@ func TestInitialRegisterAndLogin(t *testing.T) { auth, err := scene.controller.Login(scene.ctx, []byte(tc.username), tc.password) testkit.TestFatalError(t, "Login", err) - token, err := ext.ReadToken(auth, key) + token, err := ReadToken(auth, key) testkit.TestFatalError(t, "Login", err) testkit.TestValue(t, "Login", tc.username, token.Username) @@ -76,3 +76,34 @@ func TestInitialRegisterAndLogin(t *testing.T) { func remove[T any](slice []T, s int) []T { return append(slice[:s], slice[s+1:]...) } + +func TestReadWriteToken(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + key []byte + token *Token + }{ + { + name: "Normal write", + key: []byte("AES256Key-32Characters1234567890"), + token: &Token{ + UserID: 3, + Username: "username", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + data, err := WriteToken(tc.token, tc.key) + testkit.TestFatalError(t, "WriteToken", err) + + token, err := ReadToken(data, tc.key) + testkit.TestFatalError(t, "ReadToken", err) + + testkit.TestValue(t, "ReadWriteToken", token, tc.token) + }) + } +} |