2
0

5 Commits

Author SHA1 Message Date
f00027eb7c feat(vault): add encryption key mismatch detection and error handling
All checks were successful
Build and Release / Tests (push) Successful in 1m2s
Build and Release / Lint (push) Successful in 1m35s
Build and Release / Create Release (push) Successful in 1s
Added support for hex-encoded master keys (64 hex chars = 32 bytes) in crypto manager with fallback to raw bytes. Implemented comprehensive error handling for encryption/decryption failures across all vault endpoints (API and web). Created dedicated error template with user-friendly guidance for resolving key mismatch issues.
2026-02-06 19:18:18 -05:00
e88d9f2e82 ci(i18n): configure authentication for private Go modules
All checks were successful
Build and Release / Tests (push) Successful in 1m3s
Build and Release / Lint (push) Successful in 1m28s
Build and Release / Create Release (push) Successful in 0s
Sets up git credential configuration and Go environment variables (GOPRIVATE, GONOSUMDB) to enable fetching private modules from git.marketally.com and code.gitcaddy.com during builds. Uses RELEASE_TOKEN secret for authentication and disables public proxy for private repositories.
2026-02-04 14:01:06 -05:00
2aaf7223f1 feat(i18n): add vault key configuration error messages
Some checks failed
Build and Release / Tests (push) Failing after 21s
Build and Release / Lint (push) Failing after 21s
Build and Release / Create Release (push) Has been skipped
Adds English locale strings for vault encryption key warnings including fallback key usage, decryption failures, and encryption errors. Provides user-friendly explanations and remediation steps for each error scenario.
2026-02-04 13:55:08 -05:00
d9c35526bc feat(crypto): add key source tracking and fallback detection
Some checks failed
Build and Release / Lint (push) Failing after 24s
Build and Release / Tests (push) Failing after 23s
Build and Release / Create Release (push) Has been skipped
Adds tracking of master key source (app.ini, env var, file, or Gitea SECRET_KEY fallback) and exposes methods to check if fallback key is in use. This enables better visibility into which key configuration is active and helps identify when the system is using the less secure fallback option.
2026-02-04 13:47:33 -05:00
04d0d02962 chore(ci): update Go dependencies
All checks were successful
Build and Release / Tests (push) Successful in 1m37s
Build and Release / Lint (push) Successful in 1m53s
Build and Release / Create Release (push) Successful in 0s
Updates multiple dependencies including minio-go (v7.0.95 -> v7.0.98), klauspost/compress (v1.18.0 -> v1.18.2), tinylib/msgp (v1.4.0 -> v1.6.1), and various golang.org/x packages (crypto, net, sync, sys, text, mod, tools). Adds klauspost/crc32 v1.3.0 and go.yaml.in/yaml/v3 v3.0.4.
2026-01-26 00:58:05 -05:00
8 changed files with 287 additions and 64 deletions

View File

@@ -12,7 +12,9 @@ on:
workflow_dispatch:
env:
GOPROXY: https://proxy.golang.org,direct
GOPROXY: direct
GOPRIVATE: git.marketally.com,code.gitcaddy.com
GONOSUMDB: git.marketally.com,code.gitcaddy.com
GO_VERSION: "1.25"
jobs:
@@ -29,6 +31,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure git for private modules
run: |
git config --global url."https://token:${{ secrets.RELEASE_TOKEN }}@git.marketally.com/".insteadOf "https://git.marketally.com/"
- name: Setup Go
uses: actions/setup-go@v5
with:
@@ -58,6 +64,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure git for private modules
run: |
git config --global url."https://token:${{ secrets.RELEASE_TOKEN }}@git.marketally.com/".insteadOf "https://git.marketally.com/"
- name: Setup Go
uses: actions/setup-go@v5
with:

View File

@@ -7,6 +7,7 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"errors"
"io"
"os"
@@ -27,7 +28,9 @@ var (
// Manager handles encryption operations using the master KEK
type Manager struct {
masterKey []byte
masterKey []byte
usingFallback bool // true when using Gitea SECRET_KEY as fallback
keySource string
}
// NewManager creates a new crypto manager
@@ -39,14 +42,27 @@ func NewManager() *Manager {
func (m *Manager) LoadMasterKey() error {
// Priority: app.ini [vault] > env var > file > gitea secret key
key := m.loadFromSettings()
if key != nil {
m.keySource = "app.ini [vault] MASTER_KEY"
}
if key == nil {
key = m.loadFromEnv()
if key != nil {
m.keySource = "GITCADDY_VAULT_KEY environment variable"
}
}
if key == nil {
key = m.loadFromFile()
if key != nil {
m.keySource = "key file"
}
}
if key == nil {
key = m.loadFromGiteaSecret()
if key != nil {
m.keySource = "Gitea SECRET_KEY (fallback)"
m.usingFallback = true
}
}
if len(key) == 0 {
@@ -64,7 +80,7 @@ func (m *Manager) LoadMasterKey() error {
}
m.masterKey = key
log.Info("Vault master key loaded successfully")
log.Info("Vault master key loaded successfully (source: %s)", m.keySource)
return nil
}
@@ -78,7 +94,17 @@ func (m *Manager) loadFromSettings() []byte {
if keyStr == "" {
return nil
}
log.Info("Vault master key loaded from app.ini [vault] section")
// Try to hex-decode the key (expected format: 64 hex chars = 32 bytes)
if len(keyStr) == 64 {
if decoded, err := hex.DecodeString(keyStr); err == nil {
log.Info("Vault master key loaded from app.ini [vault] section (hex-decoded)")
return decoded
}
}
// Fall back to raw bytes if not valid hex
log.Info("Vault master key loaded from app.ini [vault] section (raw)")
return []byte(keyStr)
}
@@ -87,6 +113,14 @@ func (m *Manager) loadFromEnv() []byte {
if keyStr == "" {
return nil
}
// Try to hex-decode the key (expected format: 64 hex chars = 32 bytes)
if len(keyStr) == 64 {
if decoded, err := hex.DecodeString(keyStr); err == nil {
return decoded
}
}
return []byte(keyStr)
}
@@ -116,7 +150,16 @@ func (m *Manager) loadFromFile() []byte {
}
// Trim whitespace
return []byte(strings.TrimSpace(string(key)))
keyStr := strings.TrimSpace(string(key))
// Try to hex-decode the key (expected format: 64 hex chars = 32 bytes)
if len(keyStr) == 64 {
if decoded, err := hex.DecodeString(keyStr); err == nil {
return decoded
}
}
return []byte(keyStr)
}
func (m *Manager) loadFromGiteaSecret() []byte {
@@ -133,6 +176,17 @@ func (m *Manager) HasMasterKey() bool {
return len(m.masterKey) > 0
}
// IsUsingFallbackKey returns true if the master key was loaded from Gitea's SECRET_KEY
// rather than an explicit vault-specific key configuration.
func (m *Manager) IsUsingFallbackKey() bool {
return m.usingFallback
}
// KeySource returns a human-readable description of where the master key was loaded from.
func (m *Manager) KeySource() string {
return m.keySource
}
// Encrypt encrypts plaintext using AES-256-GCM
// Returns: nonce || ciphertext || tag
func (m *Manager) Encrypt(plaintext []byte, key []byte) ([]byte, error) {
@@ -239,6 +293,16 @@ func HasMasterKey() bool {
return defaultManager.HasMasterKey()
}
// IsUsingFallbackKey checks if the default manager is using Gitea's SECRET_KEY as fallback
func IsUsingFallbackKey() bool {
return defaultManager.IsUsingFallbackKey()
}
// KeySource returns the key source of the default manager
func KeySource() string {
return defaultManager.KeySource()
}
// EncryptWithMasterKey encrypts using the default manager
func EncryptWithMasterKey(plaintext []byte) ([]byte, error) {
return defaultManager.EncryptWithMasterKey(plaintext)

20
go.mod
View File

@@ -104,8 +104,9 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
@@ -117,7 +118,7 @@ require (
github.com/microsoft/go-mssqldb v1.9.3 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.95 // indirect
github.com/minio/minio-go/v7 v7.0.98 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -141,7 +142,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tinylib/msgp v1.4.0 // indirect
github.com/tinylib/msgp v1.6.1 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
@@ -155,13 +156,14 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/image v0.30.0 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect

48
go.sum
View File

@@ -407,11 +407,13 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
@@ -463,8 +465,8 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
github.com/minio/minio-go/v7 v7.0.98 h1:MeAVKjLVz+XJ28zFcuYyImNSAh8Mq725uNW4beRisi0=
github.com/minio/minio-go/v7 v7.0.98/go.mod h1:cY0Y+W7yozf0mdIclrttzo1Iiu7mEf9y7nk2uXqMOvM=
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -613,8 +615,8 @@ github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKN
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.4.0 h1:SYOeDRiydzOw9kSiwdYp9UcBgPFtLU2WDHaJXyHruf8=
github.com/tinylib/msgp v1.4.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o=
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ=
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -666,6 +668,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -678,8 +682,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
@@ -692,8 +696,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -707,8 +711,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -720,8 +724,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -746,8 +750,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -757,8 +761,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -769,8 +773,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -781,8 +785,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -11,6 +11,13 @@
"vault.config_error_title": "Vault Not Configured",
"vault.config_error_message": "The vault encryption key has not been configured. Secrets cannot be encrypted or decrypted.",
"vault.config_error_fix": "Add MASTER_KEY to the [vault] section in app.ini or set the GITCADDY_VAULT_KEY environment variable.",
"vault.fallback_key_warning_title": "Vault Using Fallback Encryption Key",
"vault.fallback_key_warning_message": "The vault is currently using Gitea's SECRET_KEY for encryption because no dedicated vault key has been configured. If the SECRET_KEY is ever changed or lost, all vault secrets will become permanently unreadable.",
"vault.fallback_key_warning_fix": "To fix this, copy the current SECRET_KEY value and set it as MASTER_KEY in the [vault] section of app.ini, or set the GITCADDY_VAULT_KEY environment variable. This ensures vault encryption remains stable even if the SECRET_KEY changes.",
"vault.decryption_error_title": "Vault Decryption Failed",
"vault.decryption_error_message": "Unable to decrypt vault secrets. The encryption key may have been changed or is incorrect.",
"vault.decryption_error_fix": "Verify that the MASTER_KEY in the [vault] section of app.ini (or the GITCADDY_VAULT_KEY environment variable) matches the key that was used when the secrets were originally created.",
"vault.encryption_error_message": "Unable to encrypt the secret value. The vault encryption key may not be configured correctly.",
"vault.secret_name": "Name",
"vault.secret_type": "Type",
"vault.secret_value": "Secret Value",
@@ -150,5 +157,13 @@
"vault.run_compare": "Compare",
"vault.unified_diff": "Unified Diff",
"vault.back_to_versions": "Back to Versions",
"vault.compare_same_version": "Cannot compare a version with itself"
"vault.compare_same_version": "Cannot compare a version with itself",
"vault.key_mismatch_title": "Encryption Key Mismatch",
"vault.key_mismatch_message": "The vault encryption key has changed since these secrets were created.",
"vault.key_mismatch_explanation": "Secrets in this vault were encrypted with a different master key than what is currently configured. This can happen if the MASTER_KEY in app.ini was changed, or if secrets were created before the key was set (using the fallback key).",
"vault.key_mismatch_solutions_title": "Possible Solutions",
"vault.key_mismatch_solution_1": "Restore the original MASTER_KEY that was used when secrets were created",
"vault.key_mismatch_solution_2": "If using the fallback key previously, temporarily remove MASTER_KEY from [vault] section to use the original key",
"vault.key_mismatch_solution_3": "Contact your administrator to migrate secrets to the new key",
"vault.key_mismatch_admin_note": "Admin: Check app.ini [vault] MASTER_KEY setting and compare with the key used when secrets were originally created."
}

View File

@@ -144,6 +144,17 @@ func (p *VaultPlugin) ConfigurationError() string {
return ""
}
// IsUsingFallbackKey returns true if the vault is using Gitea's SECRET_KEY
// as the encryption key instead of an explicit vault-specific key.
func (p *VaultPlugin) IsUsingFallbackKey() bool {
return crypto.IsUsingFallbackKey()
}
// KeySource returns a human-readable description of where the master key was loaded from.
func (p *VaultPlugin) KeySource() string {
return crypto.KeySource()
}
// Ensure VaultPlugin implements all required interfaces
var (
_ plugins.Plugin = (*VaultPlugin)(nil)

View File

@@ -38,6 +38,7 @@ const (
tplVaultCompare templates.TplName = "repo/vault/compare"
tplVaultAudit templates.TplName = "repo/vault/audit"
tplVaultTokens templates.TplName = "repo/vault/tokens"
tplVaultKeyError templates.TplName = "repo/vault/key_error"
)
// API Response types
@@ -225,6 +226,19 @@ func getWebContext(r *http.Request) *context.Context {
return context.GetWebContext(r.Context())
}
// isKeyMismatchError returns true if the error indicates an encryption key mismatch
func isKeyMismatchError(err error) bool {
return err == services.ErrEncryptionFailed || err == services.ErrDecryptionFailed
}
// showKeyMismatchError renders the key error template for encryption/decryption failures
func showKeyMismatchError(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("vault.key_mismatch_title")
ctx.Data["PageIsVault"] = true
ctx.Data["IsRepoAdmin"] = ctx.Repo.IsAdmin()
ctx.HTML(http.StatusConflict, tplVaultKeyError)
}
func requireRepoAdmin(ctx *context.APIContext) bool {
if !ctx.Repo.IsAdmin() {
ctx.JSON(http.StatusForbidden, map[string]any{
@@ -342,7 +356,7 @@ func apiListSecrets(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -400,7 +414,7 @@ func apiGetSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -458,6 +472,13 @@ func apiGetSecret(lic *license.Manager) http.HandlerFunc {
value, err := services.GetSecretValue(ctx, ctx.Repo.Repository.ID, name, version)
if err != nil {
log.Error("Failed to decrypt secret %s: %v", name, err)
if isKeyMismatchError(err) {
ctx.JSON(http.StatusConflict, map[string]any{
"error": "key_mismatch",
"message": "Encryption key mismatch. The vault master key may have changed since secrets were created.",
})
return
}
ctx.JSON(http.StatusInternalServerError, map[string]any{
"error": "decryption_failed",
"message": "Failed to decrypt secret",
@@ -492,7 +513,7 @@ func apiPutSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -563,6 +584,11 @@ func apiPutSecret(lic *license.Manager) http.HandlerFunc {
"error": "limit_reached",
"message": "Secret limit reached for this tier. Upgrade your license for more secrets.",
})
case services.ErrEncryptionFailed, services.ErrDecryptionFailed:
ctx.JSON(http.StatusConflict, map[string]any{
"error": "key_mismatch",
"message": "Encryption key mismatch. The vault master key may not be configured correctly.",
})
default:
ctx.JSON(http.StatusInternalServerError, map[string]any{
"error": "internal_error",
@@ -595,6 +621,13 @@ func apiPutSecret(lic *license.Manager) http.HandlerFunc {
UpdaterID: userID,
})
if err != nil {
if isKeyMismatchError(err) {
ctx.JSON(http.StatusConflict, map[string]any{
"error": "key_mismatch",
"message": "Encryption key mismatch. The vault master key may have changed since secrets were created.",
})
return
}
ctx.JSON(http.StatusInternalServerError, map[string]any{
"error": "internal_error",
"message": err.Error(),
@@ -613,7 +646,7 @@ func apiDeleteSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -668,7 +701,7 @@ func apiRestoreSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -723,7 +756,7 @@ func apiListVersions(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -779,7 +812,7 @@ func apiRollbackSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -838,6 +871,11 @@ func apiRollbackSecret(lic *license.Manager) http.HandlerFunc {
"error": "version_not_found",
"message": "Version not found",
})
case services.ErrEncryptionFailed, services.ErrDecryptionFailed:
ctx.JSON(http.StatusConflict, map[string]any{
"error": "key_mismatch",
"message": "Encryption key mismatch. The vault master key may have changed since secrets were created.",
})
default:
ctx.JSON(http.StatusInternalServerError, map[string]any{
"error": "internal_error",
@@ -860,7 +898,7 @@ func apiGetAudit(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -917,7 +955,7 @@ func apiListTokens(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -951,7 +989,7 @@ func apiCreateToken(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -1035,7 +1073,7 @@ func apiRevokeToken(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -1082,7 +1120,7 @@ func apiGetTokenInfo(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -1171,7 +1209,7 @@ func apiRotateKey(lic *license.Manager) http.HandlerFunc {
}
ctx := getRepoContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Repository context not found", http.StatusInternalServerError)
return
}
@@ -1205,11 +1243,16 @@ func webListSecrets(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
ctx.Data["Title"] = ctx.Tr("vault.secrets")
ctx.Data["PageIsVaultSecrets"] = true
@@ -1262,11 +1305,16 @@ func webViewSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
name := chi.URLParam(r, "name")
secret, err := services.GetSecret(ctx, ctx.Repo.Repository.ID, name)
@@ -1282,6 +1330,10 @@ func webViewSecret(lic *license.Manager) http.HandlerFunc {
value, err := services.GetSecretValue(ctx, ctx.Repo.Repository.ID, name, 0)
if err != nil {
log.Error("Failed to decrypt secret %s: %v", name, err)
if isKeyMismatchError(err) {
showKeyMismatchError(ctx)
return
}
ctx.ServerError("GetSecretValue", err)
return
}
@@ -1337,11 +1389,16 @@ func webUpdateSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
if !ctx.Repo.CanWrite(unit.TypeCode) {
ctx.NotFound(nil)
return
@@ -1365,6 +1422,10 @@ func webUpdateSecret(lic *license.Manager) http.HandlerFunc {
UpdaterID: ctx.Doer.ID,
})
if err != nil {
if isKeyMismatchError(err) {
showKeyMismatchError(ctx)
return
}
ctx.Flash.Error(ctx.Tr("vault.error_update_failed"))
ctx.Redirect(ctx.Repo.RepoLink + "/vault/secrets/" + name)
return
@@ -1382,7 +1443,7 @@ func webNewSecretForm(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
@@ -1408,11 +1469,16 @@ func webCreateSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
if !ctx.Repo.CanWrite(unit.TypeCode) {
ctx.NotFound(nil)
return
@@ -1463,6 +1529,8 @@ func webCreateSecret(lic *license.Manager) http.HandlerFunc {
ctx.Data["description"] = description
ctx.Flash.Error(ctx.Tr("vault.error_secret_limit"))
ctx.HTML(http.StatusOK, tplVaultNew)
case services.ErrEncryptionFailed, services.ErrDecryptionFailed:
showKeyMismatchError(ctx)
default:
ctx.ServerError("CreateSecret", err)
}
@@ -1481,11 +1549,16 @@ func webDeleteSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
if !ctx.Repo.CanWrite(unit.TypeCode) {
ctx.NotFound(nil)
return
@@ -1514,7 +1587,7 @@ func webRestoreSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
@@ -1547,11 +1620,16 @@ func webListVersions(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
name := chi.URLParam(r, "name")
secret, err := services.GetSecret(ctx, ctx.Repo.Repository.ID, name)
@@ -1604,7 +1682,7 @@ func webCompareVersions(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
@@ -1730,11 +1808,16 @@ func webRollbackSecret(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
if !ctx.Repo.CanWrite(unit.TypeCode) {
ctx.NotFound(nil)
return
@@ -1756,6 +1839,8 @@ func webRollbackSecret(lic *license.Manager) http.HandlerFunc {
case services.ErrVersionNotFound:
ctx.Flash.Error(ctx.Tr("vault.error_version_not_found"))
ctx.Redirect(ctx.Repo.RepoLink + "/vault/secrets/" + name)
case services.ErrEncryptionFailed, services.ErrDecryptionFailed:
showKeyMismatchError(ctx)
default:
ctx.ServerError("RollbackSecret", err)
}
@@ -1774,7 +1859,7 @@ func webViewAudit(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
@@ -1823,7 +1908,7 @@ func webListTokens(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
@@ -1855,11 +1940,16 @@ func webCreateToken(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}
if ctx.Doer == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
if !ctx.Repo.IsAdmin() {
ctx.NotFound(nil)
return
@@ -1932,7 +2022,7 @@ func webRevokeToken(lic *license.Manager) http.HandlerFunc {
}
ctx := getWebContext(r)
if ctx == nil || ctx.Repo.Repository == nil {
if ctx == nil || ctx.Repo == nil || ctx.Repo.Repository == nil {
http.Error(w, "Context not found", http.StatusInternalServerError)
return
}

View File

@@ -0,0 +1,27 @@
{{template "repo/vault/layout_head" (dict "ctxData" . "pageClass" "repository vault key-error")}}
<div class="ui placeholder segment tw-text-center">
<div class="ui icon header">
{{svg "octicon-key" 48}}
<h2>{{ctx.Locale.Tr "vault.key_mismatch_title"}}</h2>
</div>
<div class="ui warning message" style="text-align: left; max-width: 600px; margin: 1em auto;">
<div class="header">{{ctx.Locale.Tr "vault.key_mismatch_message"}}</div>
<p style="margin-top: 0.5em;">{{ctx.Locale.Tr "vault.key_mismatch_explanation"}}</p>
</div>
<div class="ui info message" style="text-align: left; max-width: 600px; margin: 1em auto;">
<div class="header">{{ctx.Locale.Tr "vault.key_mismatch_solutions_title"}}</div>
<ul class="ui list" style="margin-top: 0.5em;">
<li>{{ctx.Locale.Tr "vault.key_mismatch_solution_1"}}</li>
<li>{{ctx.Locale.Tr "vault.key_mismatch_solution_2"}}</li>
<li>{{ctx.Locale.Tr "vault.key_mismatch_solution_3"}}</li>
</ul>
</div>
{{if .IsRepoAdmin}}
<div class="ui divider"></div>
<p class="ui small text grey">{{ctx.Locale.Tr "vault.key_mismatch_admin_note"}}</p>
{{end}}
</div>
{{template "repo/vault/layout_footer" .}}