package zTinyPasswd import ( "os" "git.swzry.com/zry/pathutils" "encoding/json" "io/ioutil" "sync" "golang.org/x/crypto/bcrypt" "errors" ) type UserData struct { username string `json:"-"` Password string `json:"password"` RoleName string `json:"role"` External string `json:"extdata"` } type UserDatabase struct { UserMap map[string]UserData `json:"user_map"` External map[string]string `json:"external_data"` } type PasswdManager struct{ fromFileName bool storage_file *os.File storage UserDatabase stlock sync.RWMutex } func NewPasswdManagerFromFileName(filename string) (*PasswdManager, error) { ex,_ := pathutils.PathExists(filename) var file *os.File var err error if !ex { file,err = os.Create(filename) if err != nil { return nil, err } }else { file,err = os.OpenFile(filename, os.O_RDWR, 0666) if err != nil{ return nil, err } } p, err := NewPasswdManagerFromFileObject(file) if err == nil { p.fromFileName = true } return p, err } func NewPasswdManagerFromFileObject(file *os.File) (*PasswdManager, error) { p := &PasswdManager{ storage_file: file, fromFileName: false, } jdata,err := ioutil.ReadAll(p.storage_file) if err != nil{ return nil, err } if len(jdata) == 0{ p.storage = UserDatabase{ UserMap: make(map[string]UserData), External: make(map[string]string), } p.sync() }else { err = json.Unmarshal(jdata, &p.storage) if err != nil { return nil,err } } for k,v := range p.storage.UserMap { v.username = k } return p, nil } func (this *PasswdManager) Close() { if this.fromFileName { this.storage_file.Close() } } func (this *PasswdManager) Add(username string, passwd []byte, role string) error { brpasswd,err := bcrypt.GenerateFromPassword(passwd, 10) if err != nil{ return err } passwdinstr := string(brpasswd) this.stlock.Lock() this.storage.UserMap[username] = UserData{ username: username, Password: passwdinstr, RoleName: role, External: "", } this.stlock.Unlock() this.sync() return nil } func (this *PasswdManager) sync() error { this.stlock.RLock() defer this.stlock.RUnlock() jdata,err := json.MarshalIndent(this.storage, "", "\t") if err != nil { return err } this.storage_file.Truncate(0) this.storage_file.Seek(0,0) this.storage_file.Write(jdata) this.storage_file.Sync() return nil } func (this *PasswdManager) GetUserInfo(username string) (role string, external string){ this.stlock.RLock() defer this.stlock.RUnlock() v, ok := this.storage.UserMap[username] if ok { rs := v.RoleName es := v.External return rs,es }else { return "","" } } func (this *PasswdManager) _getUser(username string) *UserData { this.stlock.RLock() defer this.stlock.RUnlock() v, ok := this.storage.UserMap[username] if ok { return &v }else { return nil } } func (this *PasswdManager) ListUser() []string { this.stlock.RLock() defer this.stlock.RUnlock() ud := make([]string, len(this.storage.UserMap)) i := 0 for k := range this.storage.UserMap { ud[i] = k i++ } return ud } func (this *PasswdManager) Auth(username string, passwd []byte) bool { u := this._getUser(username) if u == nil { return false } this.stlock.RLock() defer this.stlock.RUnlock() err := bcrypt.CompareHashAndPassword([]byte(u.Password), passwd) if err != nil{ return false } return true } func (this *PasswdManager) ChangePassword(username string, newpass []byte) error { brpasswd,err := bcrypt.GenerateFromPassword(newpass, 10) if err != nil{ return err } passwdinstr := string(brpasswd) user := this._getUser(username) if user == nil{ return errors.New("NoSuchUser") } this.stlock.Lock() user.Password = passwdinstr this.stlock.Unlock() this.sync() return nil } func (this *PasswdManager) ChangeExternal(username string, new_external string) error { user := this._getUser(username) if user == nil{ return errors.New("NoSuchUser") } this.stlock.Lock() user.External = new_external this.stlock.Unlock() this.sync() return nil } func (this *PasswdManager) ChangeRole(username string, new_role string) error { user := this._getUser(username) if user == nil{ return errors.New("NoSuchUser") } this.stlock.Lock() user.RoleName = new_role this.stlock.Unlock() this.sync() return nil } func (this *PasswdManager) DeleteUser(username string) error { this.stlock.Lock() defer this.stlock.Unlock() _, ok := this.storage.UserMap[username] if ok{ delete(this.storage.UserMap, username) return nil }else { return errors.New("NoSuchUser") } } func (this *PasswdManager) RenameUser(oldname string, new_name string) error { user := this._getUser(oldname) if user == nil{ return errors.New("NoSuchUser") } oldn := user.username upwd := user.Password role := user.RoleName uext := user.External this.DeleteUser(oldn) this.Add(new_name, []byte(""), role) this.stlock.Lock() nu := this.storage.UserMap[new_name] nu.Password = upwd nu.External = uext this.stlock.Unlock() return nil }