github.com/benchkram/bob@v0.0.0-20240314204020-b7a57f2f9be9/pkg/auth/store.go (about)

     1  package auth
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"path"
     7  
     8  	"github.com/benchkram/errz"
     9  	"gopkg.in/yaml.v3"
    10  )
    11  
    12  var (
    13  	ErrNotFound      = errors.New("not found")
    14  	ErrAlreadyExists = errors.New("already exists")
    15  )
    16  
    17  type Store struct {
    18  	dir        string
    19  	configPath string
    20  }
    21  
    22  type YamlAuthContext struct {
    23  	Name    string `yaml:"name"`
    24  	Token   string `yaml:"token"`
    25  	Current bool   `yaml:"current"`
    26  }
    27  
    28  // New creates a filestore. The caller is responsible to pass an
    29  // existing directory.
    30  func New(dir string) *Store {
    31  	s := &Store{
    32  		dir:        dir,
    33  		configPath: path.Join(dir, "auth.yaml"),
    34  	}
    35  
    36  	return s
    37  }
    38  
    39  func (s *Store) Contexts() (names []Context, err error) {
    40  	defer errz.Recover(&err)
    41  
    42  	ctxs, err := s.contexts()
    43  	errz.Fatal(err)
    44  
    45  	names = []Context{}
    46  	for _, c := range ctxs {
    47  		names = append(names, Context{
    48  			Name:    c.Name,
    49  			Token:   c.Token,
    50  			Current: c.Current,
    51  		})
    52  	}
    53  
    54  	return names, nil
    55  }
    56  
    57  func (s *Store) Context(name string) (ctx Context, err error) {
    58  	defer errz.Recover(&err)
    59  
    60  	ctxs, err := s.contexts()
    61  	errz.Fatal(err)
    62  
    63  	for _, c := range ctxs {
    64  		if c.Name == name {
    65  			return Context{
    66  				Name:  c.Name,
    67  				Token: c.Token,
    68  			}, nil
    69  		}
    70  	}
    71  
    72  	errz.Fatal(ErrNotFound)
    73  	return
    74  }
    75  
    76  func (s *Store) CreateContext(name, token string) (err error) {
    77  	defer errz.Recover(&err)
    78  
    79  	ctxs, err := s.contexts()
    80  	errz.Fatal(err)
    81  
    82  	var exists bool
    83  	for _, c := range ctxs {
    84  		if c.Name == name {
    85  			exists = true
    86  			break
    87  		}
    88  	}
    89  
    90  	if exists {
    91  		errz.Fatal(ErrAlreadyExists)
    92  	}
    93  
    94  	ctxs = append(ctxs, &YamlAuthContext{
    95  		Name:    name,
    96  		Token:   token,
    97  		Current: len(ctxs) == 0,
    98  	})
    99  
   100  	return s.save(ctxs)
   101  }
   102  func (s *Store) DeleteContext(name string) (err error) {
   103  	defer errz.Recover(&err)
   104  
   105  	ctxs, err := s.contexts()
   106  	errz.Fatal(err)
   107  
   108  	var found bool
   109  	for i, c := range ctxs {
   110  		if c.Name == name {
   111  			found = true
   112  
   113  			// remove this context
   114  			ctxs = append(ctxs[:i], ctxs[i+1:]...)
   115  			if c.Current && len(ctxs) > 0 {
   116  				// mark the next context as current (if any left)
   117  				ctxs[len(ctxs)-1].Current = true
   118  			}
   119  
   120  			break
   121  		}
   122  	}
   123  
   124  	if !found {
   125  		errz.Fatal(ErrNotFound)
   126  	}
   127  
   128  	return s.save(ctxs)
   129  }
   130  
   131  func (s *Store) CurrentContext() (c Context, err error) {
   132  	defer errz.Recover(&err)
   133  
   134  	ctxs, err := s.contexts()
   135  	errz.Fatal(err)
   136  
   137  	for _, c := range ctxs {
   138  		if c.Current {
   139  			return Context{
   140  				Name:  c.Name,
   141  				Token: c.Token,
   142  			}, nil
   143  		}
   144  	}
   145  
   146  	return Context{}, ErrNotFound
   147  }
   148  
   149  func (s *Store) SetCurrentContext(name string) (err error) {
   150  	defer errz.Recover(&err)
   151  
   152  	ctxs, err := s.contexts()
   153  	errz.Fatal(err)
   154  
   155  	var exists bool
   156  	for _, c := range ctxs {
   157  		if c.Name == name {
   158  			exists = true
   159  		}
   160  	}
   161  
   162  	if !exists {
   163  		errz.Fatal(ErrNotFound)
   164  	}
   165  
   166  	for _, c := range ctxs {
   167  		if c.Name == name {
   168  			c.Current = true
   169  		} else {
   170  			c.Current = false
   171  		}
   172  	}
   173  
   174  	return s.save(ctxs)
   175  }
   176  
   177  func (s *Store) UpdateContext(name, token string) (err error) {
   178  	defer errz.Recover(&err)
   179  
   180  	ctxs, err := s.contexts()
   181  	errz.Fatal(err)
   182  
   183  	var found bool
   184  	for _, c := range ctxs {
   185  		if c.Name == name {
   186  			c.Token = token
   187  			found = true
   188  			break
   189  		}
   190  	}
   191  
   192  	if !found {
   193  		errz.Fatal(ErrNotFound)
   194  	}
   195  
   196  	return s.save(ctxs)
   197  }
   198  
   199  func (s *Store) contexts() (ctxs []*YamlAuthContext, err error) {
   200  	defer errz.Recover(&err)
   201  
   202  	b, err := os.ReadFile(path.Join(s.dir, "auth.yaml"))
   203  	if errors.Is(err, os.ErrNotExist) {
   204  		return []*YamlAuthContext{}, nil
   205  	}
   206  	errz.Fatal(err)
   207  
   208  	err = yaml.Unmarshal(b, &ctxs)
   209  	errz.Fatal(err)
   210  
   211  	return ctxs, nil
   212  }
   213  
   214  func (s *Store) save(ctxs []*YamlAuthContext) (err error) {
   215  	defer errz.Recover(&err)
   216  
   217  	b, err := yaml.Marshal(ctxs)
   218  	errz.Fatal(err)
   219  
   220  	err = os.WriteFile(s.configPath, b, os.ModePerm)
   221  	errz.Fatal(err)
   222  
   223  	return nil
   224  }