diff --git a/Makefile b/Makefile index 3bea273b10..0cd0d8fe6b 100644 --- a/Makefile +++ b/Makefile @@ -292,7 +292,7 @@ fmt-check: checks: checks-frontend checks-backend .PHONY: checks-frontend -checks-frontend: svg-check +checks-frontend: lockfile-check svg-check .PHONY: checks-backend checks-backend: test-vendor swagger-check swagger-validate @@ -700,6 +700,17 @@ svg-check: svg exit 1; \ fi +.PHONY: lockfile-check +lockfile-check: + npm install --package-lock-only + @diff=$$(git diff package-lock.json); \ + if [ -n "$$diff" ]; then \ + echo "package-lock.json is inconsistent with package.json"; \ + echo "Please run 'npm install --package-lock-only' and commit the result:"; \ + echo "$${diff}"; \ + exit 1; \ + fi + .PHONY: update-translations update-translations: mkdir -p ./translations diff --git a/docs/content/doc/usage/reverse-proxies.en-us.md b/docs/content/doc/usage/reverse-proxies.en-us.md index c782f1ce5c..4567b09d22 100644 --- a/docs/content/doc/usage/reverse-proxies.en-us.md +++ b/docs/content/doc/usage/reverse-proxies.en-us.md @@ -128,6 +128,7 @@ This error indicates nginx is configured to restrict the file upload size. In your nginx config file containing your Gitea proxy directive, find the `location { ... }` block for Gitea and add the line `client_max_body_size 16M;` to set this limit to 16 megabytes or any other number of choice. +If you use Git LFS, this will also limit the size of the largest file you will be able to push. ## Apache HTTPD diff --git a/models/auth/webauthn.go b/models/auth/webauthn.go index 75776f1e0e..9e09134662 100644 --- a/models/auth/webauthn.go +++ b/models/auth/webauthn.go @@ -6,7 +6,7 @@ package auth import ( "context" - "encoding/base64" + "encoding/base32" "fmt" "strings" @@ -94,7 +94,7 @@ type WebAuthnCredentialList []*WebAuthnCredential func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential { creds := make([]webauthn.Credential, 0, len(list)) for _, cred := range list { - credID, _ := base64.RawStdEncoding.DecodeString(cred.CredentialID) + credID, _ := base32.HexEncoding.DecodeString(cred.CredentialID) creds = append(creds, webauthn.Credential{ ID: credID, PublicKey: cred.PublicKey, @@ -164,13 +164,13 @@ func HasWebAuthnRegistrationsByUID(uid int64) (bool, error) { } // GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID -func GetWebAuthnCredentialByCredID(credID string) (*WebAuthnCredential, error) { - return getWebAuthnCredentialByCredID(db.DefaultContext, credID) +func GetWebAuthnCredentialByCredID(userID int64, credID string) (*WebAuthnCredential, error) { + return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID) } -func getWebAuthnCredentialByCredID(ctx context.Context, credID string) (*WebAuthnCredential, error) { +func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID string) (*WebAuthnCredential, error) { cred := new(WebAuthnCredential) - if found, err := db.GetEngine(ctx).Where("credential_id = ?", credID).Get(cred); err != nil { + if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil { return nil, err } else if !found { return nil, ErrWebAuthnCredentialNotExist{CredentialID: credID} @@ -187,7 +187,7 @@ func createCredential(ctx context.Context, userID int64, name string, cred *weba c := &WebAuthnCredential{ UserID: userID, Name: name, - CredentialID: base64.RawStdEncoding.EncodeToString(cred.ID), + CredentialID: base32.HexEncoding.EncodeToString(cred.ID), PublicKey: cred.PublicKey, AttestationType: cred.AttestationType, AAGUID: cred.Authenticator.AAGUID, diff --git a/models/auth/webauthn_test.go b/models/auth/webauthn_test.go index 572636dbbf..216bf11080 100644 --- a/models/auth/webauthn_test.go +++ b/models/auth/webauthn_test.go @@ -5,7 +5,7 @@ package auth import ( - "encoding/base64" + "encoding/base32" "testing" "code.gitea.io/gitea/models/unittest" @@ -61,7 +61,7 @@ func TestCreateCredential(t *testing.T) { res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")}) assert.NoError(t, err) assert.Equal(t, "WebAuthn Created Credential", res.Name) - bs, err := base64.RawStdEncoding.DecodeString(res.CredentialID) + bs, err := base32.HexEncoding.DecodeString(res.CredentialID) assert.NoError(t, err) assert.Equal(t, []byte("Test"), bs) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 4ee2bc839f..5aaf283bd3 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -368,6 +368,8 @@ var migrations = []Migration{ NewMigration("Add authorize column to team_unit table", addAuthorizeColForTeamUnit), // v207 -> v208 NewMigration("Add webauthn table and migrate u2f data to webauthn", addWebAuthnCred), + // v208 -> v209 + NewMigration("Use base32.HexEncoding instead of base64 encoding for cred ID as it is case insensitive", useBase32HexForCredIDInWebAuthnCredential), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v208.go b/models/migrations/v208.go new file mode 100644 index 0000000000..04bb981a4e --- /dev/null +++ b/models/migrations/v208.go @@ -0,0 +1,51 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "encoding/base32" + "encoding/base64" + + "xorm.io/xorm" +) + +func useBase32HexForCredIDInWebAuthnCredential(x *xorm.Engine) error { + + // Create webauthnCredential table + type webauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + CredentialID string `xorm:"INDEX"` + } + if err := x.Sync2(&webauthnCredential{}); err != nil { + return err + } + + var start int + regs := make([]*webauthnCredential, 0, 50) + for { + err := x.OrderBy("id").Limit(50, start).Find(®s) + if err != nil { + return err + } + + for _, reg := range regs { + credID, _ := base64.RawStdEncoding.DecodeString(reg.CredentialID) + reg.CredentialID = base32.HexEncoding.EncodeToString(credID) + + _, err := x.Update(reg) + if err != nil { + return err + } + } + + if len(regs) < 50 { + break + } + start += 50 + regs = regs[:0] + } + + return nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index e903e4c534..d8398f6d9f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -748,10 +748,9 @@ passcode_invalid = The passcode is incorrect. Try again. twofa_enrolled = Your account has been enrolled into two-factor authentication. Store your scratch token (%s) in a safe place as it is only shown once! twofa_failed_get_secret = Failed to get secret. -webauthn_desc = Security keys are hardware devices containing cryptographic keys. They can be used for two-factor authentication. Security keys must support the WebAuthn Authenticator standard. +webauthn_desc = Security keys are hardware devices containing cryptographic keys. They can be used for two-factor authentication. Security keys must support the WebAuthn Authenticator standard. webauthn_register_key = Add Security Key webauthn_nickname = Nickname -webauthn_press_button = Press the button on your security key to register it. webauthn_delete_key = Remove Security Key webauthn_delete_key_desc = If you remove a security key you can no longer sign in with it. Continue? diff --git a/package-lock.json b/package-lock.json index c9151da16c..3d62eb1ab2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": "gitea", "license": "MIT", "dependencies": { "@claviska/jquery-minicolors": "2.3.6", @@ -12,7 +13,7 @@ "codemirror": "5.64.0", "css-loader": "6.5.1", "dropzone": "6.0.0-beta.2", - "easymde": "2.15.0", + "easymde": "2.16.1", "esbuild-loader": "2.16.0", "escape-goat": "4.0.0", "fast-glob": "3.2.7", @@ -1305,9 +1306,9 @@ } }, "node_modules/@types/codemirror": { - "version": "0.0.109", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.109.tgz", - "integrity": "sha512-cSdiHeeLjvGn649lRTNeYrVCDOgDrtP+bDDSFDd1TF+i0jKGPDRozno2NOJ9lTniso+taiv4kiVS8dgM8Jm5lg==", + "version": "5.60.5", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz", + "integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==", "dependencies": { "@types/tern": "*" } @@ -1380,9 +1381,9 @@ "dev": true }, "node_modules/@types/marked": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-2.0.5.tgz", - "integrity": "sha512-shRZ7XnYFD/8n8zSjKvFdto1QNSf4tONZIlNEZGrJe8GsOE8DL/hG1Hbl8gZlfLnjS7+f5tZGIaTgfpyW38h4w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.1.tgz", + "integrity": "sha512-ZigEmCWdNUU7IjZEuQ/iaimYdDHWHfTe3kg8ORfKjyGYd9RWumPoOJRQXB0bO+XLkNwzCthW3wUIQtANaEZ1ag==" }, "node_modules/@types/minimist": { "version": "1.2.2", @@ -3780,15 +3781,15 @@ } }, "node_modules/easymde": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.15.0.tgz", - "integrity": "sha512-9jMRIVvKt1d0UjRN45yotUYECAM4xvw0TTAQw8sYDONP++keWJVnd8Xrn+V+vQEN/v9/X0SWEoo1rFSgCooGpw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.16.1.tgz", + "integrity": "sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==", "dependencies": { - "@types/codemirror": "0.0.109", - "@types/marked": "^2.0.2", - "codemirror": "^5.61.0", + "@types/codemirror": "^5.60.4", + "@types/marked": "^4.0.1", + "codemirror": "^5.63.1", "codemirror-spell-checker": "1.1.2", - "marked": "^2.0.3" + "marked": "^4.0.10" } }, "node_modules/editorconfig-checker": { @@ -7356,14 +7357,14 @@ } }, "node_modules/marked": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", + "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", "bin": { - "marked": "bin/marked" + "marked": "bin/marked.js" }, "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/mathml-tag-names": { @@ -11526,9 +11527,9 @@ } }, "@types/codemirror": { - "version": "0.0.109", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.109.tgz", - "integrity": "sha512-cSdiHeeLjvGn649lRTNeYrVCDOgDrtP+bDDSFDd1TF+i0jKGPDRozno2NOJ9lTniso+taiv4kiVS8dgM8Jm5lg==", + "version": "5.60.5", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz", + "integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==", "requires": { "@types/tern": "*" } @@ -11601,9 +11602,9 @@ "dev": true }, "@types/marked": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-2.0.5.tgz", - "integrity": "sha512-shRZ7XnYFD/8n8zSjKvFdto1QNSf4tONZIlNEZGrJe8GsOE8DL/hG1Hbl8gZlfLnjS7+f5tZGIaTgfpyW38h4w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.1.tgz", + "integrity": "sha512-ZigEmCWdNUU7IjZEuQ/iaimYdDHWHfTe3kg8ORfKjyGYd9RWumPoOJRQXB0bO+XLkNwzCthW3wUIQtANaEZ1ag==" }, "@types/minimist": { "version": "1.2.2", @@ -13474,15 +13475,15 @@ } }, "easymde": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.15.0.tgz", - "integrity": "sha512-9jMRIVvKt1d0UjRN45yotUYECAM4xvw0TTAQw8sYDONP++keWJVnd8Xrn+V+vQEN/v9/X0SWEoo1rFSgCooGpw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.16.1.tgz", + "integrity": "sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==", "requires": { - "@types/codemirror": "0.0.109", - "@types/marked": "^2.0.2", - "codemirror": "^5.61.0", + "@types/codemirror": "^5.60.4", + "@types/marked": "^4.0.1", + "codemirror": "^5.63.1", "codemirror-spell-checker": "1.1.2", - "marked": "^2.0.3" + "marked": "^4.0.10" } }, "editorconfig-checker": { @@ -16117,9 +16118,9 @@ "dev": true }, "marked": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==" + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", + "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==" }, "mathml-tag-names": { "version": "2.1.3", diff --git a/package.json b/package.json index 2f8ac8df53..86e8b9aedd 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "name": "gitea", "license": "MIT", "private": true, "type": "module", diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go index 50dcb919e5..b9e8de2ac0 100644 --- a/routers/web/auth/webauthn.go +++ b/routers/web/auth/webauthn.go @@ -5,7 +5,7 @@ package auth import ( - "encoding/base64" + "encoding/base32" "errors" "net/http" @@ -131,7 +131,7 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) { } // Success! Get the credential and update the sign count with the new value we received. - dbCred, err := auth.GetWebAuthnCredentialByCredID(base64.RawStdEncoding.EncodeToString(cred.ID)) + dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, base32.HexEncoding.EncodeToString(cred.ID)) if err != nil { ctx.ServerError("GetWebAuthnCredentialByCredID", err) return diff --git a/routers/web/user/setting/security/webauthn.go b/routers/web/user/setting/security/webauthn.go index 8d28de8c98..7e2fc7283b 100644 --- a/routers/web/user/setting/security/webauthn.go +++ b/routers/web/user/setting/security/webauthn.go @@ -38,9 +38,9 @@ func WebAuthnRegister(ctx *context.Context) { return } - _ = ctx.Session.Delete("registration") - if err := ctx.Session.Set("WebauthnName", form.Name); err != nil { - ctx.ServerError("Unable to set session key for WebauthnName", err) + _ = ctx.Session.Delete("webauthnRegistration") + if err := ctx.Session.Set("webauthnName", form.Name); err != nil { + ctx.ServerError("Unable to set session key for webauthnName", err) return } @@ -51,7 +51,7 @@ func WebAuthnRegister(ctx *context.Context) { } // Save the session data as marshaled JSON - if err = ctx.Session.Set("registration", sessionData); err != nil { + if err = ctx.Session.Set("webauthnRegistration", sessionData); err != nil { ctx.ServerError("Unable to set session", err) return } @@ -61,20 +61,20 @@ func WebAuthnRegister(ctx *context.Context) { // WebauthnRegisterPost receives the response of the security key func WebauthnRegisterPost(ctx *context.Context) { - name, ok := ctx.Session.Get("WebauthnName").(string) + name, ok := ctx.Session.Get("webauthnName").(string) if !ok || name == "" { - ctx.ServerError("Get WebauthnName", errors.New("no WebauthnName")) + ctx.ServerError("Get webauthnName", errors.New("no webauthnName")) return } // Load the session data - sessionData, ok := ctx.Session.Get("registration").(*webauthn.SessionData) + sessionData, ok := ctx.Session.Get("webauthnRegistration").(*webauthn.SessionData) if !ok || sessionData == nil { ctx.ServerError("Get registration", errors.New("no registration")) return } defer func() { - _ = ctx.Session.Delete("registration") + _ = ctx.Session.Delete("webauthnRegistration") }() // Verify that the challenge succeeded @@ -103,6 +103,8 @@ func WebauthnRegisterPost(ctx *context.Context) { ctx.ServerError("CreateCredential", err) return } + _ = ctx.Session.Delete("webauthnName") + ctx.JSON(http.StatusCreated, cred) } diff --git a/templates/user/auth/webauthn_error.tmpl b/templates/user/auth/webauthn_error.tmpl index be46ee42a0..6f2980df7c 100644 --- a/templates/user/auth/webauthn_error.tmpl +++ b/templates/user/auth/webauthn_error.tmpl @@ -12,7 +12,7 @@

{{.i18n.Tr "webauthn_error_duplicated"}}

{{.i18n.Tr "webauthn_error_empty"}}

{{.i18n.Tr "webauthn_error_timeout"}}

-
+
diff --git a/templates/user/settings/security/webauthn.tmpl b/templates/user/settings/security/webauthn.tmpl index be8f8cccda..d447ec04b3 100644 --- a/templates/user/settings/security/webauthn.tmpl +++ b/templates/user/settings/security/webauthn.tmpl @@ -28,16 +28,6 @@
- - {{template "user/auth/webauthn_error" .}}