@@ -92,24 +92,41 @@ func (i *StaticInterceptor) StreamInterceptor(srv any, ss grpc.ServerStream, _ *
9292// FileWatcherInterceptor contains details used to make authorization decisions
9393// by watching a file path that contains authorization policy in JSON format.
9494type FileWatcherInterceptor struct {
95+ options FileWatcherOptions
9596 internalInterceptor unsafe.Pointer // *StaticInterceptor
96- policyFile string
9797 policyContents []byte
98- refreshDuration time.Duration
9998 cancel context.CancelFunc
10099}
101100
101+ // FileWatcherOptions contains configuration options for the
102+ // FileWatcherInterceptor.
103+ type FileWatcherOptions struct {
104+ // PolicyFile contains a JSON string of the authorization policy.
105+ PolicyFile string
106+ // RefreshDuration is the delay between policy refreshes.
107+ RefreshDuration time.Duration
108+ // OnPolicyUpdate is a callback to be invoked when a policy is
109+ // loaded/updated. The loaded policy string is passed as an argument.
110+ OnPolicyUpdate func (string )
111+ }
112+
102113// NewFileWatcher returns a new FileWatcherInterceptor from a policy file
103114// that contains JSON string of authorization policy and a refresh duration to
104115// specify the amount of time between policy refreshes.
105116func NewFileWatcher (file string , duration time.Duration ) (* FileWatcherInterceptor , error ) {
106- if file == "" {
117+ return NewFileWatcherWithOptions (FileWatcherOptions {PolicyFile : file , RefreshDuration : duration , OnPolicyUpdate : nil })
118+ }
119+
120+ // NewFileWatcherWithOptions returns a new FileWatcherInterceptor from a set of
121+ // options.
122+ func NewFileWatcherWithOptions (options FileWatcherOptions ) (* FileWatcherInterceptor , error ) {
123+ if options .PolicyFile == "" {
107124 return nil , fmt .Errorf ("authorization policy file path is empty" )
108125 }
109- if duration <= time .Duration (0 ) {
110- return nil , fmt .Errorf ("requires refresh interval(%v) greater than 0s" , duration )
126+ if options . RefreshDuration <= time .Duration (0 ) {
127+ return nil , fmt .Errorf ("requires refresh interval(%v) greater than 0s" , options . RefreshDuration )
111128 }
112- i := & FileWatcherInterceptor {policyFile : file , refreshDuration : duration }
129+ i := & FileWatcherInterceptor {options : options }
113130 if err := i .updateInternalInterceptor (); err != nil {
114131 return nil , err
115132 }
@@ -121,7 +138,7 @@ func NewFileWatcher(file string, duration time.Duration) (*FileWatcherIntercepto
121138}
122139
123140func (i * FileWatcherInterceptor ) run (ctx context.Context ) {
124- ticker := time .NewTicker (i .refreshDuration )
141+ ticker := time .NewTicker (i .options . RefreshDuration )
125142 for {
126143 if err := i .updateInternalInterceptor (); err != nil {
127144 logger .Warningf ("authorization policy reload status err: %v" , err )
@@ -140,9 +157,9 @@ func (i *FileWatcherInterceptor) run(ctx context.Context) {
140157// constructor, if there is an error in reading the file or parsing the policy, the
141158// previous internalInterceptors will not be replaced.
142159func (i * FileWatcherInterceptor ) updateInternalInterceptor () error {
143- policyContents , err := os .ReadFile (i .policyFile )
160+ policyContents , err := os .ReadFile (i .options . PolicyFile )
144161 if err != nil {
145- return fmt .Errorf ("policyFile(%s) read failed: %v" , i .policyFile , err )
162+ return fmt .Errorf ("policyFile(%s) read failed: %v" , i .options . PolicyFile , err )
146163 }
147164 if bytes .Equal (i .policyContents , policyContents ) {
148165 return nil
@@ -155,6 +172,9 @@ func (i *FileWatcherInterceptor) updateInternalInterceptor() error {
155172 }
156173 atomic .StorePointer (& i .internalInterceptor , unsafe .Pointer (interceptor ))
157174 logger .Infof ("authorization policy reload status: successfully loaded new policy %v" , policyContentsString )
175+ if i .options .OnPolicyUpdate != nil {
176+ i .options .OnPolicyUpdate (policyContentsString )
177+ }
158178 return nil
159179}
160180
0 commit comments