github.com/infraboard/keyauth@v0.8.1/apps/user/user_ext.go (about) 1 package user 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/go-playground/validator/v10" 8 "github.com/infraboard/mcube/exception" 9 "github.com/infraboard/mcube/types/ftime" 10 "golang.org/x/crypto/bcrypt" 11 12 common "github.com/infraboard/keyauth/common/types" 13 ) 14 15 const ( 16 // DefaultExiresDays 默认多少天无登录系统就冻结该用户 17 DefaultExiresDays = 90 18 ) 19 20 // use a single instance of Validate, it caches struct info 21 var ( 22 validate = validator.New() 23 ) 24 25 // New 实例 26 func New(req *CreateAccountRequest) (*User, error) { 27 if err := req.Validate(); err != nil { 28 return nil, exception.NewBadRequest(err.Error()) 29 } 30 31 pass, err := NewHashedPassword(req.Password) 32 if err != nil { 33 return nil, exception.NewBadRequest(err.Error()) 34 } 35 36 u := &User{ 37 Domain: req.Domain, 38 CreateAt: ftime.Now().Timestamp(), 39 UpdateAt: ftime.Now().Timestamp(), 40 Profile: req.Profile, 41 DepartmentId: req.DepartmentId, 42 Account: req.Account, 43 CreateType: req.CreateType, 44 Type: req.UserType, 45 ExpiresDays: req.ExpiresDays, 46 Description: req.Description, 47 HashedPassword: pass, 48 Status: &Status{ 49 Locked: false, 50 }, 51 } 52 53 if req.DepartmentId != "" && req.Profile != nil { 54 u.IsInitialized = true 55 } 56 57 return u, nil 58 } 59 60 // NewDefaultUser 实例 61 func NewDefaultUser() *User { 62 return &User{ 63 Profile: NewProfile(), 64 Status: &Status{ 65 Locked: false, 66 }, 67 } 68 } 69 70 // Block 锁用户 71 func (u *User) Block(reason string) { 72 u.Status.Locked = true 73 u.Status.LockedReson = reason 74 u.Status.LockedTime = ftime.Now().Timestamp() 75 } 76 77 func (u *User) UnBlock() error { 78 if !u.Status.Locked { 79 return exception.NewBadRequest("user %s not block, don't unblock", u.Account) 80 } 81 82 u.Status.Locked = false 83 u.Status.UnlockTime = ftime.Now().Timestamp() 84 return nil 85 } 86 87 // Desensitize 关键数据脱敏 88 func (u *User) Desensitize() { 89 if u.HashedPassword != nil { 90 u.HashedPassword.Password = "" 91 u.HashedPassword.History = []string{} 92 } 93 return 94 } 95 96 // ChangePassword 修改用户密码 97 func (u *User) ChangePassword(old, new string, maxHistory uint, needReset bool) error { 98 // 确认旧密码 99 if err := u.HashedPassword.CheckPassword(old); err != nil { 100 return err 101 } 102 103 // 修改新密码 104 newPass, err := NewHashedPassword(new) 105 if err != nil { 106 return exception.NewBadRequest(err.Error()) 107 } 108 u.HashedPassword.Update(newPass, maxHistory, needReset) 109 return nil 110 } 111 112 // HasDepartment todo 113 func (u *User) HasDepartment() bool { 114 return u.DepartmentId != "" 115 } 116 117 // NewProfile todo 118 func NewProfile() *Profile { 119 return &Profile{} 120 } 121 122 // ValidateInitialized 判断初始化数据是否准备好了 123 func (p *Profile) ValidateInitialized() error { 124 if p.Email != "" && p.Phone != "" { 125 return nil 126 } 127 128 return fmt.Errorf("email and phone required when initial") 129 } 130 131 // Patch todo 132 func (p *Profile) Patch(data *Profile) { 133 md := NewProfile() 134 patchData, _ := json.Marshal(data) 135 oldData, _ := json.Marshal(p) 136 json.Unmarshal(oldData, md) 137 json.Unmarshal(patchData, md) 138 *p = *md 139 } 140 141 // Validate 校验请求是否合法 142 func (req *CreateAccountRequest) Validate() error { 143 return validate.Struct(req) 144 } 145 146 // NewHashedPassword 生产hash后的密码对象 147 func NewHashedPassword(password string) (*Password, error) { 148 bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10) 149 if err != nil { 150 return nil, err 151 } 152 153 return &Password{ 154 Password: string(bytes), 155 CreateAt: ftime.Now().Timestamp(), 156 UpdateAt: ftime.Now().Timestamp(), 157 }, nil 158 } 159 160 // SetExpired 密码过期 161 func (p *Password) SetExpired() { 162 p.IsExpired = true 163 } 164 165 // SetNeedReset 需要被重置 166 func (p *Password) SetNeedReset(format string, a ...interface{}) { 167 p.NeedReset = true 168 p.ResetReason = fmt.Sprintf(format, a...) 169 } 170 171 // CheckPassword 判断password 是否正确 172 func (p *Password) CheckPassword(password string) error { 173 err := bcrypt.CompareHashAndPassword([]byte(p.Password), []byte(password)) 174 if err != nil { 175 return exception.NewUnauthorized("user or password not connrect") 176 } 177 178 return nil 179 } 180 181 // IsHistory 检测是否是历史密码 182 func (p *Password) IsHistory(password string) bool { 183 for _, pass := range p.History { 184 err := bcrypt.CompareHashAndPassword([]byte(pass), []byte(password)) 185 if err == nil { 186 return true 187 } 188 } 189 190 return false 191 } 192 193 // HistoryCount 保存了几个历史密码 194 func (p *Password) HistoryCount() int { 195 return len(p.History) 196 } 197 198 func (p *Password) rotaryHistory(maxHistory uint) { 199 if uint(p.HistoryCount()) < maxHistory { 200 p.History = append(p.History, p.Password) 201 } else { 202 remainHistry := p.History[:maxHistory] 203 p.History = []string{p.Password} 204 p.History = append(p.History, remainHistry...) 205 } 206 } 207 208 // Update 更新密码 209 func (p *Password) Update(new *Password, maxHistory uint, needReset bool) { 210 p.rotaryHistory(maxHistory) 211 p.Password = new.Password 212 p.NeedReset = needReset 213 p.UpdateAt = ftime.Now().Timestamp() 214 if !needReset { 215 p.ResetReason = "" 216 } 217 } 218 219 // NewUserSet 实例 220 func NewUserSet() *Set { 221 return &Set{ 222 Items: []*User{}, 223 } 224 } 225 226 // Add todo 227 func (s *Set) Add(u *User) { 228 s.Items = append(s.Items, u) 229 } 230 231 // NewPutAccountRequest todo 232 func NewPutAccountRequest() *UpdateAccountRequest { 233 return &UpdateAccountRequest{ 234 UpdateMode: common.UpdateMode_PUT, 235 Profile: NewProfile(), 236 } 237 } 238 239 // NewPatchAccountRequest todo 240 func NewPatchAccountRequest() *UpdateAccountRequest { 241 return &UpdateAccountRequest{ 242 UpdateMode: common.UpdateMode_PATCH, 243 Profile: NewProfile(), 244 } 245 } 246 247 // Validate 更新请求校验 248 func (req *UpdateAccountRequest) Validate() error { 249 return validate.Struct(req) 250 } 251 252 func (req *UpdateAccountRequest) CheckOwner(account string) bool { 253 return req.Account == account 254 }