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  }