| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package impl |
| |
| import ( |
| "encoding/json" |
| "os" |
| "path/filepath" |
| "sync" |
| |
| "v.io/v23/services/device" |
| "v.io/v23/verror" |
| ) |
| |
| // BlessingSystemAssociationStore manages a persisted association between |
| // Vanadium blessings and system account names. |
| type BlessingSystemAssociationStore interface { |
| // SystemAccountForBlessings returns a system name from the blessing to |
| // system name association store if one exists for any of the listed |
| // blessings. |
| SystemAccountForBlessings(blessings []string) (string, bool) |
| |
| // AllBlessingSystemAssociations returns all of the current Blessing to system |
| // account associations. |
| AllBlessingSystemAssociations() ([]device.Association, error) |
| |
| // AssociateSystemAccountForBlessings associates the provided systenName with each |
| // provided blessing. |
| AssociateSystemAccountForBlessings(blessings []string, systemName string) error |
| |
| // DisassociateSystemAccountForBlessings removes associations for the provided blessings. |
| DisassociateSystemAccountForBlessings(blessings []string) error |
| } |
| |
| type association struct { |
| data map[string]string |
| filename string |
| sync.Mutex |
| } |
| |
| func (u *association) SystemAccountForBlessings(blessings []string) (string, bool) { |
| u.Lock() |
| defer u.Unlock() |
| |
| systemName := "" |
| present := false |
| |
| for _, n := range blessings { |
| if systemName, present = u.data[n]; present { |
| break |
| } |
| } |
| return systemName, present |
| } |
| |
| func (u *association) AllBlessingSystemAssociations() ([]device.Association, error) { |
| u.Lock() |
| defer u.Unlock() |
| assocs := make([]device.Association, 0) |
| |
| for k, v := range u.data { |
| assocs = append(assocs, device.Association{k, v}) |
| } |
| return assocs, nil |
| } |
| |
| func (u *association) serialize() (err error) { |
| f, err := os.OpenFile(u.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) |
| if err != nil { |
| return verror.New(verror.ErrNoExist, nil, "Could not open association file for writing", u.filename, err) |
| } |
| defer func() { |
| if closerr := f.Close(); closerr != nil { |
| err = closerr |
| } |
| }() |
| |
| enc := json.NewEncoder(f) |
| return enc.Encode(u.data) |
| } |
| |
| func (u *association) AssociateSystemAccountForBlessings(blessings []string, systemName string) error { |
| u.Lock() |
| defer u.Unlock() |
| |
| for _, n := range blessings { |
| u.data[n] = systemName |
| } |
| return u.serialize() |
| } |
| |
| func (u *association) DisassociateSystemAccountForBlessings(blessings []string) error { |
| u.Lock() |
| defer u.Unlock() |
| |
| for _, n := range blessings { |
| delete(u.data, n) |
| } |
| return u.serialize() |
| } |
| |
| func NewBlessingSystemAssociationStore(root string) (BlessingSystemAssociationStore, error) { |
| nddir := filepath.Join(root, "device-manager", "device-data") |
| if err := os.MkdirAll(nddir, os.FileMode(0700)); err != nil { |
| return nil, verror.New(verror.ErrNoExist, nil, "Could not create device-data directory", nddir, err) |
| } |
| msf := filepath.Join(nddir, "associated.accounts") |
| |
| f, err := os.Open(msf) |
| if err != nil && os.IsExist(err) { |
| return nil, verror.New(verror.ErrNoExist, nil, "Could not open association file", msf, err) |
| |
| } |
| defer f.Close() |
| |
| a := &association{filename: msf, data: make(map[string]string)} |
| |
| if err == nil { |
| dec := json.NewDecoder(f) |
| err := dec.Decode(&a.data) |
| if err != nil { |
| return nil, verror.New(verror.ErrNoExist, nil, "Could not read association file", msf, err) |
| } |
| } |
| return BlessingSystemAssociationStore(a), nil |
| } |