mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-23 02:53:32 +00:00
Merge pull request #316 from abhinavxd/fix/key-rotation
Don't fatal on encryption_key rotation
This commit is contained in:
+10
-7
@@ -641,7 +641,7 @@ func (m *Manager) encryptInboxConfig(config json.RawMessage) (json.RawMessage, e
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
// decryptInboxConfig decrypts sensitive fields in the inbox config JSON.
|
||||
// Decrypt failures clear the field so the app stays usable across encryption_key rotation.
|
||||
func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, error) {
|
||||
if len(config) == 0 {
|
||||
return config, nil
|
||||
@@ -652,14 +652,15 @@ func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, e
|
||||
return nil, fmt.Errorf("unmarshalling config: %w", err)
|
||||
}
|
||||
|
||||
// Decrypt SMTP passwords
|
||||
if smtpSlice, ok := cfg["smtp"].([]any); ok {
|
||||
for i, smtpItem := range smtpSlice {
|
||||
if smtpMap, ok := smtpItem.(map[string]any); ok {
|
||||
if password, ok := smtpMap["password"].(string); ok && password != "" {
|
||||
decrypted, err := crypto.Decrypt(password, m.encryptionKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypting SMTP password at index %d: %w", i, err)
|
||||
m.lo.Error("error decrypting SMTP password, clearing field", "index", i, "error", err)
|
||||
smtpMap["password"] = ""
|
||||
continue
|
||||
}
|
||||
smtpMap["password"] = decrypted
|
||||
}
|
||||
@@ -667,14 +668,15 @@ func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, e
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt IMAP passwords
|
||||
if imapSlice, ok := cfg["imap"].([]any); ok {
|
||||
for i, imapItem := range imapSlice {
|
||||
if imapMap, ok := imapItem.(map[string]any); ok {
|
||||
if password, ok := imapMap["password"].(string); ok && password != "" {
|
||||
decrypted, err := crypto.Decrypt(password, m.encryptionKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypting IMAP password at index %d: %w", i, err)
|
||||
m.lo.Error("error decrypting IMAP password, clearing field", "index", i, "error", err)
|
||||
imapMap["password"] = ""
|
||||
continue
|
||||
}
|
||||
imapMap["password"] = decrypted
|
||||
}
|
||||
@@ -682,14 +684,15 @@ func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, e
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt OAuth fields if present
|
||||
if oauthMap, ok := cfg["oauth"].(map[string]any); ok {
|
||||
fields := []string{"client_secret", "access_token", "refresh_token"}
|
||||
for _, fieldName := range fields {
|
||||
if fieldValue, ok := oauthMap[fieldName].(string); ok && fieldValue != "" {
|
||||
decrypted, err := crypto.Decrypt(fieldValue, m.encryptionKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypting OAuth %s: %w", fieldName, err)
|
||||
m.lo.Error("error decrypting OAuth field, clearing field", "field", fieldName, "error", err)
|
||||
oauthMap[fieldName] = ""
|
||||
continue
|
||||
}
|
||||
oauthMap[fieldName] = decrypted
|
||||
}
|
||||
|
||||
+29
-43
@@ -77,12 +77,8 @@ func (o *Manager) Get(id int) (models.OIDC, error) {
|
||||
return oidc, envelope.NewError(envelope.GeneralError, o.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
|
||||
// Decrypt sensitive fields
|
||||
if err := o.decryptOIDC(&oidc); err != nil {
|
||||
return models.OIDC{}, envelope.NewError(envelope.GeneralError, o.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
o.decryptOIDC(&oidc)
|
||||
|
||||
// Set logo and redirect URL.
|
||||
oidc.SetProviderLogo()
|
||||
rootURL, err := o.setting.GetAppRootURL()
|
||||
if err != nil {
|
||||
@@ -106,10 +102,7 @@ func (o *Manager) GetAll() ([]models.OIDC, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decrypt sensitive fields
|
||||
if err := o.decryptOIDCSlice(oidc); err != nil {
|
||||
return nil, envelope.NewError(envelope.GeneralError, o.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
o.decryptOIDCSlice(oidc)
|
||||
|
||||
// Set logo and redirect URL for each record
|
||||
for i := range oidc {
|
||||
@@ -133,10 +126,7 @@ func (o *Manager) Create(oidc models.OIDC) (models.OIDC, error) {
|
||||
return models.OIDC{}, envelope.NewError(envelope.GeneralError, o.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
|
||||
// Decrypt fields before returning (ignore errors as these are non-critical for creation response)
|
||||
if err := o.decryptOIDC(&createdOIDC); err != nil {
|
||||
o.lo.Error("error decrypting after creation", "error", err)
|
||||
}
|
||||
o.decryptOIDC(&createdOIDC)
|
||||
|
||||
return createdOIDC, nil
|
||||
}
|
||||
@@ -165,10 +155,7 @@ func (o *Manager) Update(id int, oidc models.OIDC) (models.OIDC, error) {
|
||||
return models.OIDC{}, envelope.NewError(envelope.GeneralError, o.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
|
||||
// Decrypt fields before returning (ignore errors as these are non-critical for update response)
|
||||
if err := o.decryptOIDC(&updatedOIDC); err != nil {
|
||||
o.lo.Error("error decrypting after update", "error", err)
|
||||
}
|
||||
o.decryptOIDC(&updatedOIDC)
|
||||
|
||||
return updatedOIDC, nil
|
||||
}
|
||||
@@ -200,32 +187,31 @@ func (o *Manager) encryptOIDC(clientID, clientSecret string) (encClientID, encCl
|
||||
return encClientID, encClientSecret, nil
|
||||
}
|
||||
|
||||
// decryptOIDC decrypts sensitive OIDC fields in-place.
|
||||
// Returns an error if decryption of any field fails.
|
||||
func (o *Manager) decryptOIDC(oidc *models.OIDC) error {
|
||||
var err error
|
||||
oidc.ClientID, err = crypto.Decrypt(oidc.ClientID, o.encryptionKey)
|
||||
if err != nil {
|
||||
o.lo.Error("error decrypting client_id", "error", err, "oidc_id", oidc.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
oidc.ClientSecret, err = crypto.Decrypt(oidc.ClientSecret, o.encryptionKey)
|
||||
if err != nil {
|
||||
o.lo.Error("error decrypting client_secret", "error", err, "oidc_id", oidc.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decryptOIDCSlice decrypts sensitive fields for all OIDC records in a slice.
|
||||
// Returns an error if decryption of any record fails.
|
||||
func (o *Manager) decryptOIDCSlice(oidcs []models.OIDC) error {
|
||||
for i := range oidcs {
|
||||
if err := o.decryptOIDC(&oidcs[i]); err != nil {
|
||||
return err
|
||||
// Decrypt failures clear the field so the app stays usable across encryption_key rotation.
|
||||
func (o *Manager) decryptOIDC(oidc *models.OIDC) {
|
||||
if oidc.ClientID != "" {
|
||||
decrypted, err := crypto.Decrypt(oidc.ClientID, o.encryptionKey)
|
||||
if err != nil {
|
||||
o.lo.Error("error decrypting client_id, clearing field", "error", err, "oidc_id", oidc.ID)
|
||||
oidc.ClientID = ""
|
||||
} else {
|
||||
oidc.ClientID = decrypted
|
||||
}
|
||||
}
|
||||
|
||||
if oidc.ClientSecret != "" {
|
||||
decrypted, err := crypto.Decrypt(oidc.ClientSecret, o.encryptionKey)
|
||||
if err != nil {
|
||||
o.lo.Error("error decrypting client_secret, clearing field", "error", err, "oidc_id", oidc.ID)
|
||||
oidc.ClientSecret = ""
|
||||
} else {
|
||||
oidc.ClientSecret = decrypted
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Manager) decryptOIDCSlice(oidcs []models.OIDC) {
|
||||
for i := range oidcs {
|
||||
o.decryptOIDC(&oidcs[i])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user