@@ -7,15 +7,31 @@ import (
7
7
"path/filepath"
8
8
"strings"
9
9
"sync"
10
+ "time"
10
11
11
12
"github.com/jcmturner/gokrb5/v8/client"
12
13
"github.com/jcmturner/gokrb5/v8/config"
13
14
"github.com/jcmturner/gokrb5/v8/credentials"
14
15
)
15
16
16
17
var (
18
+ // kerberosClient caches Kerberos clients keyed by resolved ccache path.
19
+ // Clients are reused unless the associated ccache file changes.
17
20
kerberosClient sync.Map // map[string]*client.Client
18
- kerberosErr sync.Map // map[string]error
21
+
22
+ // kerberosErr caches errors encountered when loading Kerberos clients.
23
+ // Prevents repeated attempts for paths that previously failed.
24
+ kerberosErr sync.Map // map[string]error
25
+
26
+ // kerberosCredModTime tracks the last known modification time of ccache files.
27
+ // Used to detect changes and trigger credential refresh.
28
+ kerberosCredModTime sync.Map // map[string]time.Time
29
+ )
30
+
31
+ var (
32
+ loadCCacheFunc = credentials .LoadCCache
33
+ newClientFromCCache = client .NewFromCCache
34
+ loadKrbConfig = loadKerberosConfig
19
35
)
20
36
21
37
func resolveCcachePath (ccachePath string ) (string , error ) {
@@ -65,30 +81,45 @@ func createKerberosClient(ccachePath string) (*client.Client, error) {
65
81
return nil , err
66
82
}
67
83
68
- // check if we already have a client or an error for this ccache path
69
- if errVal , ok := kerberosErr .Load (ccachePath ); ok {
70
- return nil , errVal .(error )
84
+ // Check if the ccache file is modified since last check
85
+ stat , statErr := os .Stat (ccachePath )
86
+ if statErr != nil {
87
+ kerberosErr .Store (ccachePath , statErr )
88
+ return nil , statErr
71
89
}
72
- if clientVal , ok := kerberosClient .Load (ccachePath ); ok {
73
- return clientVal .(* client.Client ), nil
90
+ mtime := stat .ModTime ()
91
+
92
+ if oldCredModTimeVal , ok := kerberosCredModTime .Load (ccachePath ); ok {
93
+ if oldMtime , ok := oldCredModTimeVal .(time.Time ); ok && oldMtime .Equal (mtime ) {
94
+ // ccache hasn't changed — return cached client or error
95
+ if errVal , ok := kerberosErr .Load (ccachePath ); ok {
96
+ return nil , errVal .(error )
97
+ }
98
+ if clientVal , ok := kerberosClient .Load (ccachePath ); ok {
99
+ return clientVal .(* client.Client ), nil
100
+ }
101
+ }
74
102
}
75
103
76
- // create a new client if not found in the map
77
- cfg , err := loadKerberosConfig ()
104
+ // ccache changed or no valid cached client — reload credentials
105
+ cfg , err := loadKrbConfig ()
78
106
if err != nil {
79
107
kerberosErr .Store (ccachePath , err )
80
108
return nil , err
81
109
}
82
- ccache , err := credentials . LoadCCache (ccachePath )
110
+ ccache , err := loadCCacheFunc (ccachePath )
83
111
if err != nil {
84
112
kerberosErr .Store (ccachePath , err )
85
113
return nil , err
86
114
}
87
- cl , err := client . NewFromCCache (ccache , cfg )
115
+ cl , err := newClientFromCCache (ccache , cfg )
88
116
if err != nil {
89
117
kerberosErr .Store (ccachePath , err )
90
118
return nil , err
91
119
}
120
+
92
121
kerberosClient .Store (ccachePath , cl )
122
+ kerberosErr .Delete (ccachePath )
123
+ kerberosCredModTime .Store (ccachePath , mtime )
93
124
return cl , nil
94
125
}
0 commit comments