Skip to content

Commit 1b741c8

Browse files
committed
feat: enhance restic permission validation and add unit tests
1 parent cab6664 commit 1b741c8

File tree

4 files changed

+143
-17
lines changed

4 files changed

+143
-17
lines changed

config/config.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3788,10 +3788,24 @@ func (conf *Config) CheckKeepWithin() error {
37883788
return nil
37893789
}
37903790

3791+
func isValidResticMode(value int) bool {
3792+
if value == 0 {
3793+
return true
3794+
}
3795+
if value < 600 || value > 777 {
3796+
return false
3797+
}
3798+
_, err := strconv.ParseUint(strconv.Itoa(value), 8, 32)
3799+
return err == nil
3800+
}
3801+
37913802
func parseResticMode(value int, defaultMode os.FileMode) os.FileMode {
37923803
if value <= 0 {
37933804
return defaultMode
37943805
}
3806+
if !isValidResticMode(value) {
3807+
return defaultMode
3808+
}
37953809

37963810
parsed, err := strconv.ParseUint(strconv.Itoa(value), 8, 32)
37973811
if err != nil {
@@ -3802,21 +3816,11 @@ func parseResticMode(value int, defaultMode os.FileMode) os.FileMode {
38023816
}
38033817

38043818
func (conf *Config) ValidateResticPermissions() error {
3805-
if conf.BackupResticDirMode < 0 {
3806-
return NewValidationError("backup-restic-dir-mode", conf.BackupResticDirMode, "expected octal value like 700")
3807-
}
3808-
if conf.BackupResticDirMode != 0 {
3809-
if _, err := strconv.ParseUint(strconv.Itoa(conf.BackupResticDirMode), 8, 32); err != nil {
3810-
return NewValidationError("backup-restic-dir-mode", conf.BackupResticDirMode, "expected octal value like 700")
3811-
}
3812-
}
3813-
if conf.BackupResticFileMode < 0 {
3814-
return NewValidationError("backup-restic-file-mode", conf.BackupResticFileMode, "expected octal value like 600")
3819+
if !isValidResticMode(conf.BackupResticDirMode) {
3820+
return NewValidationError("backup-restic-dir-mode", conf.BackupResticDirMode, "expected octal value in 6xx/7xx range, like 700")
38153821
}
3816-
if conf.BackupResticFileMode != 0 {
3817-
if _, err := strconv.ParseUint(strconv.Itoa(conf.BackupResticFileMode), 8, 32); err != nil {
3818-
return NewValidationError("backup-restic-file-mode", conf.BackupResticFileMode, "expected octal value like 600")
3819-
}
3822+
if !isValidResticMode(conf.BackupResticFileMode) {
3823+
return NewValidationError("backup-restic-file-mode", conf.BackupResticFileMode, "expected octal value in 6xx/7xx range, like 600")
38203824
}
38213825
return nil
38223826
}

config/restic_permissions_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package config
2+
3+
import (
4+
"os"
5+
"testing"
6+
)
7+
8+
func TestParseResticMode(t *testing.T) {
9+
defaultMode := os.FileMode(0700)
10+
11+
if got := parseResticMode(700, defaultMode); got != 0700 {
12+
t.Fatalf("expected 700 to parse as 0700, got %#o", got)
13+
}
14+
if got := parseResticMode(600, defaultMode); got != 0600 {
15+
t.Fatalf("expected 600 to parse as 0600, got %#o", got)
16+
}
17+
if got := parseResticMode(755, defaultMode); got != 0755 {
18+
t.Fatalf("expected 755 to parse as 0755, got %#o", got)
19+
}
20+
if got := parseResticMode(400, defaultMode); got != defaultMode {
21+
t.Fatalf("expected 400 to fallback to default, got %#o", got)
22+
}
23+
if got := parseResticMode(888, defaultMode); got != defaultMode {
24+
t.Fatalf("expected 888 to fallback to default, got %#o", got)
25+
}
26+
}
27+
28+
func TestValidateResticPermissions(t *testing.T) {
29+
conf := &Config{}
30+
31+
conf.BackupResticDirMode = 700
32+
conf.BackupResticFileMode = 600
33+
if err := conf.ValidateResticPermissions(); err != nil {
34+
t.Fatalf("expected valid permissions, got error: %v", err)
35+
}
36+
37+
conf.BackupResticDirMode = 400
38+
conf.BackupResticFileMode = 600
39+
if err := conf.ValidateResticPermissions(); err == nil {
40+
t.Fatalf("expected error for invalid dir mode 400")
41+
}
42+
43+
conf.BackupResticDirMode = 700
44+
conf.BackupResticFileMode = 888
45+
if err := conf.ValidateResticPermissions(); err == nil {
46+
t.Fatalf("expected error for invalid file mode 888")
47+
}
48+
49+
conf.BackupResticDirMode = 0
50+
conf.BackupResticFileMode = 0
51+
if err := conf.ValidateResticPermissions(); err != nil {
52+
t.Fatalf("expected zero values to be valid, got error: %v", err)
53+
}
54+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Restic Permission Validation
2+
3+
## Summary
4+
5+
Restic permission modes accept only octal digit values in the 6xx or 7xx range
6+
(e.g., 600, 700, 750, 755). This keeps configuration consistent with OpenSVC's
7+
permission inputs (string values like "700"/"600").
8+
9+
## Entry Points
10+
11+
- config/config.go
12+
- parseResticMode() converts octal digit values to os.FileMode.
13+
- ValidateResticPermissions() enforces allowed ranges and returns
14+
configuration validation errors.
15+
- server/api_cluster.go
16+
- API updates for backup-restic-dir-mode and backup-restic-file-mode
17+
validate inputs and return errors if invalid.
18+
- cluster/cluster_bck.go
19+
- StartResticManager() calls ValidateResticPermissions() and logs a warning
20+
on invalid inputs.
21+
22+
## Rules
23+
24+
- Valid values: 600-777 (octal digits only)
25+
- Zero value: 0 means "use defaults" (0700 for dirs, 0600 for files)
26+
- Invalid values (examples): 400, 888, -1, 0o700 (parsed to 448 and rejected)
27+
28+
## Behavior
29+
30+
- API updates with invalid values return errors and do not apply changes.
31+
- Invalid values from config files are rejected by validation; defaults are
32+
used when values are zero or invalid.
33+
34+
## Rationale
35+
36+
- Aligns with OpenSVC permission formatting (string octal digits).
37+
- Avoids ambiguous interpretation of decimal values.
38+
- Ensures stronger defaults for security (owner-only permissions).
39+
40+
## Examples
41+
42+
Valid:
43+
- backup-restic-dir-mode = 700
44+
- backup-restic-file-mode = 600
45+
46+
Invalid:
47+
- backup-restic-dir-mode = 400
48+
- backup-restic-file-mode = 888
49+

server/api_cluster.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2804,20 +2804,39 @@ func (repman *ReplicationManager) setClusterSetting(mycluster *cluster.Cluster,
28042804
val, _ := strconv.Atoi(value)
28052805
mycluster.Conf.BackupResticPurgeOldestOnDiskThreshold = val
28062806
case "backup-restic-timeout":
2807-
val, _ := strconv.Atoi(value)
2807+
val, err := strconv.Atoi(value)
2808+
if err != nil {
2809+
return fmt.Errorf("invalid value for backup-restic-timeout: %q", value)
2810+
}
28082811
mycluster.Conf.BackupResticTimeout = val
28092812
if mycluster.ResticManager != nil {
28102813
mycluster.ResticManager.SetOperationTimeout(mycluster.Conf.GetResticTimeout())
28112814
}
28122815
case "backup-restic-dir-mode":
2813-
val, _ := strconv.Atoi(value)
2816+
val, err := strconv.Atoi(value)
2817+
if err != nil {
2818+
return fmt.Errorf("invalid value for backup-restic-dir-mode: %q", value)
2819+
}
2820+
oldValue := mycluster.Conf.BackupResticDirMode
28142821
mycluster.Conf.BackupResticDirMode = val
2822+
if err := mycluster.Conf.ValidateResticPermissions(); err != nil {
2823+
mycluster.Conf.BackupResticDirMode = oldValue
2824+
return err
2825+
}
28152826
if mycluster.ResticManager != nil {
28162827
mycluster.ResticManager.SetPermissions(mycluster.Conf.GetResticDirMode(), mycluster.Conf.GetResticFileMode())
28172828
}
28182829
case "backup-restic-file-mode":
2819-
val, _ := strconv.Atoi(value)
2830+
val, err := strconv.Atoi(value)
2831+
if err != nil {
2832+
return fmt.Errorf("invalid value for backup-restic-file-mode: %q", value)
2833+
}
2834+
oldValue := mycluster.Conf.BackupResticFileMode
28202835
mycluster.Conf.BackupResticFileMode = val
2836+
if err := mycluster.Conf.ValidateResticPermissions(); err != nil {
2837+
mycluster.Conf.BackupResticFileMode = oldValue
2838+
return err
2839+
}
28212840
if mycluster.ResticManager != nil {
28222841
mycluster.ResticManager.SetPermissions(mycluster.Conf.GetResticDirMode(), mycluster.Conf.GetResticFileMode())
28232842
}

0 commit comments

Comments
 (0)