Skip to content

Commit

Permalink
Rework security module, replace JWT library, invalidate JWT tokens si…
Browse files Browse the repository at this point in the history
…gned for Chrly v4, generate RSA key in runtime when not provided via configuration
  • Loading branch information
erickskrauch committed Feb 1, 2024
1 parent 1134028 commit 10c11bc
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 321 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ replace github.com/asaskevich/EventBus v0.0.0-20200330115301-33b3bc6a7ddc => git

// Main dependencies
require (
github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2
github.com/asaskevich/EventBus v0.0.0-20200330115301-33b3bc6a7ddc
github.com/brunomvsouza/singleflight v0.4.0
github.com/defval/di v1.12.0
github.com/etherlabsio/healthcheck/v2 v2.0.0
github.com/getsentry/raven-go v0.2.1-0.20190419175539-919484f041ea
github.com/go-playground/validator/v10 v10.17.0
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/gorilla/mux v1.8.1
github.com/jellydator/ttlcache/v3 v3.1.1
github.com/mediocregopher/radix/v4 v4.1.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2 h1:koK7z0nSsRiRiBWwa+E714Puh+DO+ZRdIyAXiXzL+lg=
github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
github.com/brunomvsouza/singleflight v0.4.0 h1:9dNcTeYoXSus3xbZEM0EEZ11EcCRjUZOvVW8rnDMG5Y=
github.com/brunomvsouza/singleflight v0.4.0/go.mod h1:8RYo9j5WQRupmsnUz5DlUWZxDLNi+t9Zhj3EZFmns7I=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
Expand Down Expand Up @@ -31,6 +29,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
Expand Down
17 changes: 9 additions & 8 deletions internal/cmd/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,31 @@ package cmd

import (
"fmt"
"log"

"ely.by/chrly/internal/http"
"ely.by/chrly/internal/security"

"github.com/spf13/cobra"
)

var tokenCmd = &cobra.Command{
Use: "token",
Short: "Creates a new token, which allows to interact with Chrly API",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
container := shouldGetContainer()
var auth *http.JwtAuth
var auth *security.Jwt
err := container.Resolve(&auth)
if err != nil {
log.Fatal(err)
return err
}

token, err := auth.NewToken(http.SkinScope)
token, err := auth.NewToken(security.ProfileScope)
if err != nil {
log.Fatalf("Unable to create new token. The error is %v\n", err)
return fmt.Errorf("Unable to create a new token. The error is %v\n", err)
}

fmt.Printf("%s\n", token)
fmt.Println(token)

return nil
},
}

Expand Down
9 changes: 2 additions & 7 deletions internal/di/di.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package di
import "github.com/defval/di"

func New() (*di.Container, error) {
container, err := di.New(
return di.New(
config,
dispatcher,
logger,
Expand All @@ -12,11 +12,6 @@ func New() (*di.Container, error) {
handlers,
profilesDi,
server,
signer,
securityDiOptions,
)
if err != nil {
return nil, err
}

return container, nil
}
21 changes: 14 additions & 7 deletions internal/di/signer.go → internal/di/security.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
package di

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"strings"

"ely.by/chrly/internal/http"
. "ely.by/chrly/internal/signer"
"ely.by/chrly/internal/security"

"github.com/defval/di"
"github.com/spf13/viper"
)

var signer = di.Options(
var securityDiOptions = di.Options(
di.Provide(newTexturesSigner,
di.As(new(http.TexturesSigner)),
),
)

func newTexturesSigner(config *viper.Viper) (*Signer, error) {
func newTexturesSigner(config *viper.Viper) (*security.Signer, error) {
keyStr := config.GetString("chrly.signing.key")
if keyStr == "" {
return nil, errors.New("chrly.signing.key must be set in order to sign textures")
// TODO: log a message about the generated signing key and the way to specify it permanently
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}

return security.NewSigner(privateKey), nil
}

var keyBytes []byte
Expand All @@ -40,10 +47,10 @@ func newTexturesSigner(config *viper.Viper) (*Signer, error) {
}

rawPem, _ := pem.Decode(keyBytes)
key, err := x509.ParsePKCS1PrivateKey(rawPem.Bytes)
privateKey, err := x509.ParsePKCS1PrivateKey(rawPem.Bytes)
if err != nil {
return nil, err
}

return &Signer{Key: key}, nil
return security.NewSigner(privateKey), nil
}
8 changes: 3 additions & 5 deletions internal/di/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,21 @@ import (
"github.com/spf13/viper"

. "ely.by/chrly/internal/http"
"ely.by/chrly/internal/security"
)

var server = di.Options(
di.Provide(newAuthenticator, di.As(new(Authenticator))),
di.Provide(newServer),
)

func newAuthenticator(config *viper.Viper, emitter Emitter) (*JwtAuth, error) {
func newAuthenticator(config *viper.Viper) (*security.Jwt, error) {
key := config.GetString("chrly.secret")
if key == "" {
return nil, errors.New("chrly.secret must be set in order to use authenticator")
}

return &JwtAuth{
Key: []byte(key),
Emitter: emitter,
}, nil
return security.NewJwt([]byte(key)), nil
}

type serverParams struct {
Expand Down
11 changes: 2 additions & 9 deletions internal/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ type Authenticator interface {
Authenticate(req *http.Request) error
}

// The current middleware implementation doesn't check the scope assigned to the token.
// For now there is only one scope and at this moment I don't want to spend time on it
func CreateAuthenticationMiddleware(checker Authenticator) mux.MiddlewareFunc {
return func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -137,12 +139,3 @@ func apiForbidden(resp http.ResponseWriter, reason string) {
})
_, _ = resp.Write(result)
}

func apiNotFound(resp http.ResponseWriter, reason string) {
resp.WriteHeader(http.StatusNotFound)
resp.Header().Set("Content-Type", "application/json")
result, _ := json.Marshal([]interface{}{
reason,
})
_, _ = resp.Write(result)
}
78 changes: 0 additions & 78 deletions internal/http/jwt.go

This file was deleted.

Loading

0 comments on commit 10c11bc

Please sign in to comment.