@@ -65,7 +65,7 @@ func (i *StaticInterceptor) UnaryInterceptor(ctx context.Context, req any, _ *gr
6565 if logger .V (2 ) {
6666 logger .Infof ("unauthorized RPC request rejected: %v" , err )
6767 }
68- return nil , status .Errorf (codes .PermissionDenied , "unauthorized RPC request rejected" )
68+ return nil , status .Errorf (codes .PermissionDenied , "authz: unauthorized RPC request rejected" )
6969 }
7070 return nil , err
7171 }
@@ -82,7 +82,7 @@ func (i *StaticInterceptor) StreamInterceptor(srv any, ss grpc.ServerStream, _ *
8282 if logger .V (2 ) {
8383 logger .Infof ("unauthorized RPC request rejected: %v" , err )
8484 }
85- return status .Errorf (codes .PermissionDenied , "unauthorized RPC request rejected" )
85+ return status .Errorf (codes .PermissionDenied , "authz: unauthorized RPC request rejected" )
8686 }
8787 return err
8888 }
@@ -92,24 +92,54 @@ 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+ //
104+ // # Experimental
105+ //
106+ // Notice: This API is EXPERIMENTAL and may be changed or removed in a
107+ // later release.
108+ type FileWatcherOptions struct {
109+ // PolicyFile contains a JSON string of the authorization policy.
110+ PolicyFile string
111+ // RefreshDuration is the delay between policy refreshes.
112+ RefreshDuration time.Duration
113+ // OnPolicyUpdate is a callback to be invoked when a policy is
114+ // loaded/updated. The loaded policy string is passed as an argument.
115+ //
116+ // The callback is executed synchronously, so should complete quickly or
117+ // risk blocking future updates.
118+ OnPolicyUpdate func (string )
119+ }
120+
102121// NewFileWatcher returns a new FileWatcherInterceptor from a policy file
103122// that contains JSON string of authorization policy and a refresh duration to
104123// specify the amount of time between policy refreshes.
105124func NewFileWatcher (file string , duration time.Duration ) (* FileWatcherInterceptor , error ) {
106- if file == "" {
107- return nil , fmt .Errorf ("authorization policy file path is empty" )
125+ return NewFileWatcherWithOptions (FileWatcherOptions {PolicyFile : file , RefreshDuration : duration , OnPolicyUpdate : nil })
126+ }
127+
128+ // NewFileWatcherWithOptions returns a new FileWatcherInterceptor from a set of
129+ // options.
130+ //
131+ // # Experimental
132+ //
133+ // Notice: This API is EXPERIMENTAL and may be changed or removed in a
134+ // later release.
135+ func NewFileWatcherWithOptions (options FileWatcherOptions ) (* FileWatcherInterceptor , error ) {
136+ if options .PolicyFile == "" {
137+ return nil , fmt .Errorf ("authz: authorization policy file path is empty" )
108138 }
109- if duration <= time .Duration (0 ) {
110- return nil , fmt .Errorf ("requires refresh interval(%v) greater than 0s" , duration )
139+ if options . RefreshDuration <= time .Duration (0 ) {
140+ return nil , fmt .Errorf ("authz: requires refresh interval(%v) greater than 0s" , options . RefreshDuration )
111141 }
112- i := & FileWatcherInterceptor {policyFile : file , refreshDuration : duration }
142+ i := & FileWatcherInterceptor {options : options }
113143 if err := i .updateInternalInterceptor (); err != nil {
114144 return nil , err
115145 }
@@ -121,7 +151,7 @@ func NewFileWatcher(file string, duration time.Duration) (*FileWatcherIntercepto
121151}
122152
123153func (i * FileWatcherInterceptor ) run (ctx context.Context ) {
124- ticker := time .NewTicker (i .refreshDuration )
154+ ticker := time .NewTicker (i .options . RefreshDuration )
125155 for {
126156 if err := i .updateInternalInterceptor (); err != nil {
127157 logger .Warningf ("authorization policy reload status err: %v" , err )
@@ -140,9 +170,9 @@ func (i *FileWatcherInterceptor) run(ctx context.Context) {
140170// constructor, if there is an error in reading the file or parsing the policy, the
141171// previous internalInterceptors will not be replaced.
142172func (i * FileWatcherInterceptor ) updateInternalInterceptor () error {
143- policyContents , err := os .ReadFile (i .policyFile )
173+ policyContents , err := os .ReadFile (i .options . PolicyFile )
144174 if err != nil {
145- return fmt .Errorf ("policyFile(%s) read failed: %v" , i .policyFile , err )
175+ return fmt .Errorf ("policyFile(%s) read failed: %v" , i .options . PolicyFile , err )
146176 }
147177 if bytes .Equal (i .policyContents , policyContents ) {
148178 return nil
@@ -155,6 +185,9 @@ func (i *FileWatcherInterceptor) updateInternalInterceptor() error {
155185 }
156186 atomic .StorePointer (& i .internalInterceptor , unsafe .Pointer (interceptor ))
157187 logger .Infof ("authorization policy reload status: successfully loaded new policy %v" , policyContentsString )
188+ if i .options .OnPolicyUpdate != nil {
189+ i .options .OnPolicyUpdate (policyContentsString )
190+ }
158191 return nil
159192}
160193
0 commit comments