diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 30e5628..ad64bfe 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -14,6 +14,7 @@ import ( "npm/internal/entity/user" "npm/internal/errors" "npm/internal/jobqueue" + "npm/internal/jwt" "npm/internal/logger" ) @@ -26,6 +27,11 @@ func main() { config.Init(&version, &commit, &sentryDSN) database.Migrate(func() { + if err := jwt.LoadKeys(); err != nil { + logger.Error("KeysError", err) + os.Exit(1) + } + setting.ApplySettings() checkSetup() diff --git a/backend/embed/migrations/sqlite/20201013035318_initial_schema.sql b/backend/embed/migrations/sqlite/20201013035318_initial_schema.sql index ac4bfe8..aeee284 100644 --- a/backend/embed/migrations/sqlite/20201013035318_initial_schema.sql +++ b/backend/embed/migrations/sqlite/20201013035318_initial_schema.sql @@ -1,5 +1,15 @@ -- migrate:up +CREATE TABLE IF NOT EXISTS `keys` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at INTEGER NOT NULL DEFAULT 0, + updated_at INTEGER NOT NULL DEFAULT 0, + is_deleted INTEGER NOT NULL DEFAULT 0, + public_key TEXT NOT NULL, + private_key TEXT NOT NULL +); + CREATE TABLE IF NOT EXISTS `user` ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/backend/go.mod b/backend/go.mod index 4e1b53d..18e2a9d 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -12,8 +12,10 @@ require ( github.com/getsentry/sentry-go v0.21.0 github.com/glebarez/sqlite v1.8.0 github.com/go-chi/chi v4.1.2+incompatible + github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/cors v1.2.1 github.com/go-chi/jwtauth v4.0.4+incompatible + github.com/go-chi/jwtauth/v5 v5.1.0 github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -32,16 +34,23 @@ require ( require ( github.com/alexflint/go-scalar v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/glebarez/go-sqlite v1.21.1 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/goccy/go-json v0.9.11 // indirect github.com/google/uuid v1.3.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.3.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/lestrrat-go/blackmagic v1.0.1 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c // indirect + github.com/lestrrat-go/jwx/v2 v2.0.6 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/pdebug/v3 v3.0.1 // indirect github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb // indirect diff --git a/backend/go.sum b/backend/go.sum index 64bed1c..ad17622 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -11,6 +11,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e h1:2R8DvYLNr5DL25eWwpOdPno1eIbTNjJC0d7v8ti5cus= @@ -27,14 +30,20 @@ github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/jwtauth v4.0.4+incompatible h1:LGIxg6YfvSBzxU2BljXbrzVc1fMlgqSKBQgKOGAVtPY= github.com/go-chi/jwtauth v4.0.4+incompatible/go.mod h1:Q5EIArY/QnD6BdS+IyDw7B2m6iNbnPxtfd6/BcmtWbs= +github.com/go-chi/jwtauth/v5 v5.1.0 h1:wJyf2YZ/ohPvNJBwPOzZaQbyzwgMZZceE1m8FOzXLeA= +github.com/go-chi/jwtauth/v5 v5.1.0/go.mod h1:MA93hc1au3tAQwCKry+fI4LqJ5MIVN4XSsglOo+lSc8= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -64,9 +73,20 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kyoh86/richgo v0.3.8/go.mod h1:2C8POkF1H04iTOG2Tp1yyZhspCME9nN3cir3VXJ02II= github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs= +github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= +github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= +github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c h1:pGh5EFIfczeDHwgMHgfwjhZzL+8/E3uZF6T7vER/W8c= github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c/go.mod h1:xw2Gm4Mg+ST9s8fHR1VkUIyOJMJnSloRZlPQB+wyVpY= +github.com/lestrrat-go/jwx/v2 v2.0.6 h1:RlyYNLV892Ed7+FTfj1ROoF6x7WxL965PGTHso/60G0= +github.com/lestrrat-go/jwx/v2 v2.0.6/go.mod h1:aVrGuwEr3cp2Prw6TtQvr8sQxe+84gruID5C9TxT64Q= github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/pdebug/v3 v3.0.1 h1:3G5sX/aw/TbMTtVc9U7IHBWRZtMvwvBziF1e4HoQtv8= @@ -126,18 +146,25 @@ github.com/vrischmann/envconfig v1.3.0 h1:4XIvQTXznxmWMnjouj0ST5lFo/WAYf5Exgl3x8 github.com/vrischmann/envconfig v1.3.0/go.mod h1:bbvxFYJdRSpXrhS63mBFtKJzkDiNkyArOLXtY6q0kuI= github.com/wacul/ptr v1.0.0/go.mod h1:BD0gjsZrCwtoR+yWDB9v2hQ8STlq9tT84qKfa+3txOc= github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/backend/internal/api/middleware/auth.go b/backend/internal/api/middleware/auth.go index 1cd91cf..6f581ed 100644 --- a/backend/internal/api/middleware/auth.go +++ b/backend/internal/api/middleware/auth.go @@ -13,7 +13,7 @@ import ( "npm/internal/logger" "npm/internal/util" - "github.com/go-chi/jwtauth" + "github.com/go-chi/jwtauth/v5" ) // DecodeAuth decodes an auth header @@ -29,7 +29,7 @@ func DecodeAuth() func(http.Handler) http.Handler { } tokenAuth := jwtauth.New("RS256", privateKey, publicKey) - return jwtauth.Verifier(tokenAuth) + return jwtauth.Verify(tokenAuth, jwtauth.TokenFromHeader) } // Enforce is a authentication middleware to enforce access from the @@ -44,13 +44,14 @@ func Enforce(permission string) func(http.Handler) http.Handler { token, claims, err := jwtauth.FromContext(ctx) if err != nil { + logger.Debug("EnforceError: %+v", err) h.ResultErrorJSON(w, r, http.StatusUnauthorized, err.Error(), nil) return } userID := uint(claims["uid"].(float64)) _, enabled := user.IsEnabled(userID) - if token == nil || !token.Valid || !enabled { + if token == nil || !enabled { h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil) return } diff --git a/backend/internal/api/middleware/cors.go b/backend/internal/api/middleware/cors.go index 17ff515..15489b9 100644 --- a/backend/internal/api/middleware/cors.go +++ b/backend/internal/api/middleware/cors.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/go-chi/chi" + "github.com/go-chi/chi/v5" ) var methodMap = []string{ diff --git a/backend/internal/api/router.go b/backend/internal/api/router.go index e1be1cd..894c957 100644 --- a/backend/internal/api/router.go +++ b/backend/internal/api/router.go @@ -12,8 +12,8 @@ import ( "npm/internal/logger" "npm/internal/serverevents" - "github.com/go-chi/chi" - chiMiddleware "github.com/go-chi/chi/middleware" + "github.com/go-chi/chi/v5" + chiMiddleware "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" ) diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 9206222..f430923 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -26,7 +26,6 @@ func Init(version, commit, sentryDSN *string) { initLogger(*sentryDSN) logger.Info("Build Version: %s (%s)", Version, Commit) createDataFolders() - loadKeys() } // InitIPRanges will initialise the config for the ipranges command @@ -79,11 +78,3 @@ func initLogger(sentryDSN string) { func GetLogLevel() logger.Level { return logLevel } - -func isError(errorClass string, err error) bool { - if err != nil { - logger.Error(errorClass, err) - return true - } - return false -} diff --git a/backend/internal/config/keys.go b/backend/internal/config/keys.go deleted file mode 100644 index 609961d..0000000 --- a/backend/internal/config/keys.go +++ /dev/null @@ -1,111 +0,0 @@ -package config - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/asn1" - "encoding/pem" - "fmt" - "os" - - "npm/internal/logger" -) - -var keysFolder string -var publicKeyFile string -var privateKeyFile string - -func loadKeys() { - // check if keys folder exists in data folder - keysFolder = fmt.Sprintf("%s/keys", Configuration.DataFolder) - publicKeyFile = fmt.Sprintf("%s/public.key", keysFolder) - privateKeyFile = fmt.Sprintf("%s/private.key", keysFolder) - - if _, err := os.Stat(keysFolder); os.IsNotExist(err) { - // nolint:errcheck,gosec - os.Mkdir(keysFolder, 0700) - } - - // check if keys exist on disk - _, publicKeyErr := os.Stat(publicKeyFile) - _, privateKeyErr := os.Stat(privateKeyFile) - - // generate keys if either one doesn't exist - if os.IsNotExist(publicKeyErr) || os.IsNotExist(privateKeyErr) { - generateKeys() - saveKeys() - } - - // Load keys from disk - // nolint:gosec - publicKeyBytes, publicKeyBytesErr := os.ReadFile(publicKeyFile) - // nolint:gosec - privateKeyBytes, privateKeyBytesErr := os.ReadFile(privateKeyFile) - PublicKey = string(publicKeyBytes) - PrivateKey = string(privateKeyBytes) - - if isError("PublicKeyReadError", publicKeyBytesErr) || isError("PrivateKeyReadError", privateKeyBytesErr) || PublicKey == "" || PrivateKey == "" { - logger.Warn("There was an error loading keys, proceeding to generate new RSA keys") - generateKeys() - saveKeys() - } -} - -func generateKeys() { - reader := rand.Reader - bitSize := 4096 - - key, err := rsa.GenerateKey(reader, bitSize) - if isError("RSAGenerateError", err) { - return - } - - privateKey := &pem.Block{ - Type: "PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(key), - } - - privateKeyBuffer := new(bytes.Buffer) - err = pem.Encode(privateKeyBuffer, privateKey) - if isError("PrivatePEMEncodeError", err) { - return - } - - asn1Bytes, err2 := asn1.Marshal(key.PublicKey) - if isError("RSAMarshalError", err2) { - return - } - - publicKey := &pem.Block{ - Type: "PUBLIC KEY", - Bytes: asn1Bytes, - } - - publicKeyBuffer := new(bytes.Buffer) - err = pem.Encode(publicKeyBuffer, publicKey) - if isError("PublicPEMEncodeError", err) { - return - } - - PublicKey = publicKeyBuffer.String() - PrivateKey = privateKeyBuffer.String() - logger.Info("Generated new RSA keys") -} - -func saveKeys() { - err := os.WriteFile(publicKeyFile, []byte(PublicKey), 0600) - if err != nil { - logger.Error("PublicKeyWriteError", err) - } else { - logger.Info("Saved Public Key: %s", publicKeyFile) - } - - err = os.WriteFile(privateKeyFile, []byte(PrivateKey), 0600) - if err != nil { - logger.Error("PrivateKeyWriteError", err) - } else { - logger.Info("Saved Private Key: %s", privateKeyFile) - } -} diff --git a/backend/internal/config/vars.go b/backend/internal/config/vars.go index 6103e3c..59e1dee 100644 --- a/backend/internal/config/vars.go +++ b/backend/internal/config/vars.go @@ -16,12 +16,6 @@ var IsSetup bool // ErrorReporting defines whether we will send errors to Sentry var ErrorReporting bool -// PublicKey is the public key -var PublicKey string - -// PrivateKey is the private key -var PrivateKey string - var logLevel logger.Level type log struct { diff --git a/backend/internal/jwt/keys.go b/backend/internal/jwt/keys.go index 39ec7c4..b082c4a 100644 --- a/backend/internal/jwt/keys.go +++ b/backend/internal/jwt/keys.go @@ -1,11 +1,14 @@ package jwt import ( + "bytes" + "crypto/rand" "crypto/rsa" "crypto/x509" + "encoding/asn1" "encoding/pem" - "npm/internal/config" + "npm/internal/logger" "github.com/rotisserie/eris" ) @@ -21,12 +24,12 @@ func GetPrivateKey() (*rsa.PrivateKey, error) { if privateKey == nil { var blankKey *rsa.PrivateKey - if config.PrivateKey == "" { + if currentKeys.PrivateKey == "" { return blankKey, eris.New("Could not get Private Key from configuration") } var err error - privateKey, err = LoadPemPrivateKey(config.PrivateKey) + privateKey, err = LoadPemPrivateKey(currentKeys.PrivateKey) if err != nil { return blankKey, err } @@ -48,12 +51,12 @@ func GetPublicKey() (*rsa.PublicKey, error) { if publicKey == nil { var blankKey *rsa.PublicKey - if config.PublicKey == "" { - return blankKey, eris.New("Could not get Public Key filename, check environment variables") + if currentKeys.PublicKey == "" { + return blankKey, eris.New("Could not get Public Key from configuration") } var err error - publicKey, err = LoadPemPublicKey(config.PublicKey) + publicKey, err = LoadPemPublicKey(currentKeys.PublicKey) if err != nil { return blankKey, err } @@ -85,3 +88,48 @@ func LoadPemPublicKey(content string) (*rsa.PublicKey, error) { return publicKeyFileImported, nil } + +func generateKeys() (KeysModel, error) { + m := KeysModel{} + reader := rand.Reader + bitSize := 4096 + + key, err := rsa.GenerateKey(reader, bitSize) + if err != nil { + return m, err + } + + privateKey := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + + privateKeyBuffer := new(bytes.Buffer) + err = pem.Encode(privateKeyBuffer, privateKey) + if err != nil { + return m, err + } + + asn1Bytes, err := asn1.Marshal(key.PublicKey) + if err != nil { + return m, err + } + + publicKey := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: asn1Bytes, + } + + publicKeyBuffer := new(bytes.Buffer) + err = pem.Encode(publicKeyBuffer, publicKey) + if err != nil { + return m, err + } + + m.PublicKey = publicKeyBuffer.String() + m.PrivateKey = privateKeyBuffer.String() + + logger.Info("Generated new RSA keys") + + return m, nil +} diff --git a/backend/internal/jwt/keys_db.go b/backend/internal/jwt/keys_db.go new file mode 100644 index 0000000..45961e7 --- /dev/null +++ b/backend/internal/jwt/keys_db.go @@ -0,0 +1,54 @@ +package jwt + +import ( + "npm/internal/database" + "npm/internal/entity" + "npm/internal/logger" +) + +var currentKeys KeysModel + +// KeysModel is the model +type KeysModel struct { + entity.ModelBase + PublicKey string `gorm:"column:public_key"` + PrivateKey string `gorm:"column:private_key"` +} + +// TableName overrides the table name used by gorm +func (KeysModel) TableName() string { + return "keys" +} + +// LoadByID will load from an ID +func (m *KeysModel) LoadLatest() error { + db := database.GetDB() + result := db.Order("created_at DESC").First(&m) + return result.Error +} + +// Save will save this model to the DB +func (m *KeysModel) Save() error { + db := database.GetDB() + result := db.Save(m) + return result.Error +} + +// LoadKeys will load from the database, or generate and save new ones +func LoadKeys() error { + // Try to find in db + if err := currentKeys.LoadLatest(); err != nil { + // Keys probably don't exist, so we need to generate some + if currentKeys, err = generateKeys(); err != nil { + return err + } + + // and save them + if err = currentKeys.Save(); err != nil { + return err + } + } + logger.Debug("private: %s", currentKeys.PrivateKey) + logger.Debug("public: %s", currentKeys.PublicKey) + return nil +}