github.com/wrgl/wrgl@v0.14.0/pkg/auth/fs/authz.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright © 2022 Wrangle Ltd
     3  
     4  package authfs
     5  
     6  import (
     7  	_ "embed"
     8  	"net/http"
     9  	"os"
    10  	"path/filepath"
    11  	"sync"
    12  
    13  	"github.com/casbin/casbin/v2"
    14  	"github.com/casbin/casbin/v2/model"
    15  	fileadapter "github.com/casbin/casbin/v2/persist/file-adapter"
    16  	"github.com/fsnotify/fsnotify"
    17  	"github.com/wrgl/wrgl/pkg/local"
    18  )
    19  
    20  //go:embed casbin_model.conf
    21  var casbinModel string
    22  
    23  type AuthzStore struct {
    24  	e       *casbin.Enforcer
    25  	rootDir string
    26  	watcher *fsnotify.Watcher
    27  	mutex   sync.Mutex
    28  	ErrChan chan error
    29  	done    chan struct{}
    30  	wg      sync.WaitGroup
    31  }
    32  
    33  func NewAuthzStore(rd *local.RepoDir) (s *AuthzStore, err error) {
    34  	s = &AuthzStore{
    35  		rootDir: rd.FullPath,
    36  		ErrChan: make(chan error, 1),
    37  		done:    make(chan struct{}, 1),
    38  	}
    39  	if err := s.read(); err != nil {
    40  		return nil, err
    41  	}
    42  	s.watcher, err = rd.Watcher()
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	go s.watch()
    47  	return s, nil
    48  }
    49  
    50  func (s *AuthzStore) filepath() string {
    51  	return filepath.Join(s.rootDir, "authz.csv")
    52  }
    53  
    54  func (s *AuthzStore) read() error {
    55  	m, err := model.NewModelFromString(casbinModel)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	fp := s.filepath()
    60  	if _, err := os.Stat(fp); os.IsNotExist(err) {
    61  		f, err := os.OpenFile(fp, os.O_CREATE, 0600)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		if err := f.Close(); err != nil {
    66  			return err
    67  		}
    68  	}
    69  	e, err := casbin.NewEnforcer(m, fileadapter.NewAdapter(fp))
    70  	if err != nil {
    71  		return err
    72  	}
    73  	s.e = e
    74  	return nil
    75  }
    76  
    77  func (s *AuthzStore) reload() error {
    78  	s.mutex.Lock()
    79  	defer s.mutex.Unlock()
    80  	return s.e.LoadPolicy()
    81  }
    82  
    83  func (s *AuthzStore) watch() {
    84  	s.wg.Add(1)
    85  	defer s.wg.Done()
    86  	for {
    87  		select {
    88  		case event, ok := <-s.watcher.Events:
    89  			if !ok {
    90  				return
    91  			}
    92  			if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
    93  				if event.Name == s.filepath() {
    94  					if err := s.reload(); err != nil {
    95  						s.ErrChan <- err
    96  					}
    97  				}
    98  			}
    99  		case err, ok := <-s.watcher.Errors:
   100  			if !ok {
   101  				return
   102  			}
   103  			s.ErrChan <- err
   104  		case <-s.done:
   105  			return
   106  		}
   107  	}
   108  }
   109  
   110  func (s *AuthzStore) AddPolicy(email, act string) error {
   111  	_, err := s.e.AddPolicy(email, "-", act)
   112  	return err
   113  }
   114  
   115  func (s *AuthzStore) RemovePolicy(email, act string) error {
   116  	_, err := s.e.RemovePolicy(email, "-", act)
   117  	return err
   118  }
   119  
   120  func (s *AuthzStore) Authorized(r *http.Request, email, scope string) (bool, error) {
   121  	ok, err := s.e.Enforce(email, "-", scope)
   122  	if err != nil {
   123  		return false, err
   124  	}
   125  	return ok, nil
   126  }
   127  
   128  func (s *AuthzStore) Flush() error {
   129  	s.mutex.Lock()
   130  	defer s.mutex.Unlock()
   131  	return s.e.SavePolicy()
   132  }
   133  
   134  func (s *AuthzStore) ListPolicies(email string) (scopes []string, err error) {
   135  	policies := s.e.GetFilteredPolicy(0, email, "-")
   136  	scopes = make([]string, len(policies))
   137  	for i, sl := range policies {
   138  		scopes[i] = sl[2]
   139  	}
   140  	return scopes, nil
   141  }
   142  
   143  func (s *AuthzStore) Close() {
   144  	close(s.done)
   145  	s.wg.Wait()
   146  	close(s.ErrChan)
   147  }